diff options
| author | acdenisSK <[email protected]> | 2017-10-14 22:41:25 +0200 |
|---|---|---|
| committer | acdenisSK <[email protected]> | 2017-10-14 22:41:25 +0200 |
| commit | cae014758a1d1e926a71679f02e32601c57f8d52 (patch) | |
| tree | 39270dbc2df916a91c3c4272600fd082a2604516 /src | |
| parent | Switch to parking_lot::{Mutex, RwLock} (diff) | |
| parent | Release v0.4.1 (diff) | |
| download | serenity-cae014758a1d1e926a71679f02e32601c57f8d52.tar.xz serenity-cae014758a1d1e926a71679f02e32601c57f8d52.zip | |
Update to account for changes made in 0.4.1
Diffstat (limited to 'src')
| -rw-r--r-- | src/builder/create_embed.rs | 2 | ||||
| -rw-r--r-- | src/cache/mod.rs | 6 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_manager.rs | 13 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_queuer.rs | 14 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_runner.rs | 8 | ||||
| -rw-r--r-- | src/client/dispatch.rs | 2 | ||||
| -rw-r--r-- | src/client/mod.rs | 12 | ||||
| -rw-r--r-- | src/framework/standard/args.rs | 49 | ||||
| -rw-r--r-- | src/framework/standard/help_commands.rs | 143 | ||||
| -rw-r--r-- | src/framework/standard/mod.rs | 16 | ||||
| -rw-r--r-- | src/http/ratelimiting.rs | 5 | ||||
| -rw-r--r-- | src/internal/macros.rs | 7 | ||||
| -rw-r--r-- | src/internal/timer.rs | 4 | ||||
| -rw-r--r-- | src/lib.rs | 24 | ||||
| -rw-r--r-- | src/model/channel/message.rs | 4 | ||||
| -rw-r--r-- | src/model/event.rs | 34 | ||||
| -rw-r--r-- | src/model/guild/member.rs | 34 | ||||
| -rw-r--r-- | src/model/guild/mod.rs | 239 | ||||
| -rw-r--r-- | src/model/permissions.rs | 2 | ||||
| -rw-r--r-- | src/utils/colour.rs | 6 | ||||
| -rw-r--r-- | src/voice/audio.rs | 2 | ||||
| -rw-r--r-- | src/voice/connection.rs | 2 |
22 files changed, 434 insertions, 194 deletions
diff --git a/src/builder/create_embed.rs b/src/builder/create_embed.rs index f7dc0b1..f197507 100644 --- a/src/builder/create_embed.rs +++ b/src/builder/create_embed.rs @@ -69,7 +69,7 @@ impl CreateEmbed { pub fn colour<C: Into<Colour>>(mut self, colour: C) -> Self { self.0.insert( "color".to_string(), - Value::Number(Number::from(colour.into().0 as u64)), + Value::Number(Number::from(u64::from(colour.into().0))), ); CreateEmbed(self.0) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 6b93d41..238ca97 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -303,15 +303,15 @@ impl Cache { let id = id.into(); if let Some(channel) = self.channels.get(&id) { - return Some(Channel::Guild(channel.clone())); + return Some(Channel::Guild(Arc::clone(channel))); } if let Some(private_channel) = self.private_channels.get(&id) { - return Some(Channel::Private(private_channel.clone())); + return Some(Channel::Private(Arc::clone(private_channel))); } if let Some(group) = self.groups.get(&id) { - return Some(Channel::Group(group.clone())); + return Some(Channel::Group(Arc::clone(group))); } None diff --git a/src/client/bridge/gateway/shard_manager.rs b/src/client/bridge/gateway/shard_manager.rs index 4a50b56..6e3b285 100644 --- a/src/client/bridge/gateway/shard_manager.rs +++ b/src/client/bridge/gateway/shard_manager.rs @@ -32,6 +32,7 @@ pub struct ShardManager { impl ShardManager { #[cfg(feature = "framework")] + #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] pub fn new<H>( shard_index: u64, shard_init: u64, @@ -49,15 +50,15 @@ impl ShardManager { let runners = Arc::new(Mutex::new(HashMap::new())); let mut shard_queuer = ShardQueuer { - data: data.clone(), - event_handler: event_handler.clone(), - framework: framework.clone(), + data: Arc::clone(&data), + event_handler: Arc::clone(&event_handler), + framework: Arc::clone(&framework), last_start: None, manager_tx: thread_tx.clone(), - runners: runners.clone(), + runners: Arc::clone(&runners), rx: shard_queue_rx, - token: token.clone(), - ws_url: ws_url.clone(), + token: Arc::clone(&token), + ws_url: Arc::clone(&ws_url), threadpool, }; diff --git a/src/client/bridge/gateway/shard_queuer.rs b/src/client/bridge/gateway/shard_queuer.rs index f75e35d..cb3f749 100644 --- a/src/client/bridge/gateway/shard_queuer.rs +++ b/src/client/bridge/gateway/shard_queuer.rs @@ -80,16 +80,20 @@ impl<H: EventHandler + Send + Sync + 'static> ShardQueuer<H> { fn start(&mut self, shard_id: ShardId, shard_total: ShardId) -> Result<()> { let shard_info = [shard_id.0, shard_total.0]; - let shard = Shard::new(self.ws_url.clone(), self.token.clone(), shard_info)?; + let shard = Shard::new( + Arc::clone(&self.ws_url), + Arc::clone(&self.token), + shard_info, + )?; let locked = Arc::new(Mutex::new(shard)); let mut runner = feature_framework! {{ ShardRunner::new( - locked.clone(), + Arc::clone(&locked), self.manager_tx.clone(), - self.framework.clone(), - self.data.clone(), - self.event_handler.clone(), + Arc::clone(&self.framework), + Arc::clone(&self.data), + Arc::clone(&self.event_handler), self.threadpool.clone(), ) } else { diff --git a/src/client/bridge/gateway/shard_runner.rs b/src/client/bridge/gateway/shard_runner.rs index 53d4b80..b14e48b 100644 --- a/src/client/bridge/gateway/shard_runner.rs +++ b/src/client/bridge/gateway/shard_runner.rs @@ -110,12 +110,12 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { let (event, successful) = self.recv_event(); if let Some(event) = event { - let data = self.data.clone(); - let event_handler = self.event_handler.clone(); - let shard = self.shard.clone(); + let data = Arc::clone(&self.data); + let event_handler = Arc::clone(&self.event_handler); + let shard = Arc::clone(&self.shard); feature_framework! {{ - let framework = self.framework.clone(); + let framework = Arc::clone(&self.framework); self.threadpool.execute(|| { dispatch( diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs index aec6283..c296b70 100644 --- a/src/client/dispatch.rs +++ b/src/client/dispatch.rs @@ -204,7 +204,7 @@ fn handle_event<H: EventHandler + 'static>(event: Event, let cache = CACHE.read(); if cache.unavailable_guilds.is_empty() { - let context = context(conn.clone(), data.clone()); + let context = context(Arc::clone(&conn), Arc::clone(&data)); let guild_amount = cache .guilds diff --git a/src/client/mod.rs b/src/client/mod.rs index c17e284..6f6b5ba 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -766,16 +766,16 @@ impl<H: EventHandler + Send + Sync + 'static> Client<H> { shard_data[0], shard_data[1] - shard_data[0] + 1, shard_data[2], - gateway_url.clone(), - self.token.clone(), - self.data.clone(), - self.event_handler.clone(), + Arc::clone(&gateway_url), + Arc::clone(&self.token), + Arc::clone(&self.data), + Arc::clone(&self.event_handler), #[cfg(feature = "framework")] - self.framework.clone(), + Arc::clone(&self.framework), self.threadpool.clone(), ); - self.shard_runners = manager.runners.clone(); + self.shard_runners = Arc::clone(&manager.runners); if let Err(why) = manager.initialize() { error!("Failed to boot a shard: {:?}", why); diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 30264b3..f59908a 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -53,6 +53,10 @@ impl<E: StdError> fmt::Display for Error<E> { type Result<T, E> = ::std::result::Result<T, Error<E>>; +/// A utility struct for handling arguments of a command. +/// +/// General functionality is done via removing an item, parsing it, then returning it; this however +/// can be mitigated with the `*_n` methods, which just parse and return. #[derive(Clone, Debug)] pub struct Args { delimiter: String, @@ -116,10 +120,7 @@ impl Args { let mut vec = Vec::with_capacity(i as usize); for _ in 0..i { - vec.push(match self.delimiter_split.shift() { - Some(x) => x, - None => return None, - }); + vec.push(try_opt!(self.delimiter_split.shift())); } Some(vec) @@ -161,16 +162,14 @@ impl Args { } /// Empty outs the internal vector while parsing (if necessary) and returning them - pub fn list<T: FromStr>(self) -> Result<Vec<T>, T::Err> + pub fn list<T: FromStr>(mut self) -> Result<Vec<T>, T::Err> where T::Err: StdError { - if self.delimiter_split.is_empty() { - return Err(Error::Eos); - } + Iter::<T>::new(&mut self).collect() + } - self.delimiter_split - .into_iter() - .map(|s| s.parse::<T>().map_err(Error::Parse)) - .collect() + /// Provides an iterator of items: (`T: FromStr`) `Result<T, T::Err>`. + pub fn iter<T: FromStr>(&mut self) -> Iter<T> where T::Err: StdError { + Iter::new(self) } /// This method is just `internal_vector.join(delimiter)` @@ -221,3 +220,29 @@ impl ::std::ops::Deref for Args { fn deref(&self) -> &Self::Target { &self.delimiter_split } } + +use std::marker::PhantomData; + +/// Provides `list`'s functionality, but as an iterator. +pub struct Iter<'a, T: FromStr> where T::Err: StdError { + args: &'a mut Args, + _marker: PhantomData<T>, +} + +impl<'a, T: FromStr> Iter<'a, T> where T::Err: StdError { + fn new(args: &'a mut Args) -> Self { + Iter { args, _marker: PhantomData } + } +} + +impl<'a, T: FromStr> Iterator for Iter<'a, T> where T::Err: StdError { + type Item = Result<T, T::Err>; + + fn next(&mut self) -> Option<Self::Item> { + if self.args.is_empty() { + None + } else { + Some(self.args.single::<T>()) + } + } +} diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs index dbf25a9..ce90e74 100644 --- a/src/framework/standard/help_commands.rs +++ b/src/framework/standard/help_commands.rs @@ -29,7 +29,7 @@ use std::fmt::Write; use super::command::InternalCommand; use super::{Args, Command, CommandGroup, CommandOrAlias, CommandError}; use client::Context; -use model::{ChannelId, Guild, Member, Message}; +use model::{ChannelId, Message}; use utils::Colour; use framework::standard::{has_correct_roles, has_correct_permissions}; @@ -51,14 +51,25 @@ fn remove_aliases(cmds: &HashMap<String, CommandOrAlias>) -> HashMap<&String, &I result } -/// Checks whether a user is member of required roles +/// Checks whether a user is member of required roles /// and given the required permissions. -fn has_all_requirements(cmd: &Command, guild: &Guild, member: &Member, msg: &Message) -> bool { - if cmd.allowed_roles.is_empty() { - has_correct_permissions(cmd, msg) - } else { - has_correct_roles(cmd, guild, member) && has_correct_permissions(cmd, msg) +pub fn has_all_requirements(cmd: &Command, msg: &Message) -> bool { + if let Some(guild) = msg.guild() { + let guild = guild.read(); + + if let Some(member) = guild.members.get(&msg.author.id) { + + if let Ok(permissions) = member.permissions() { + + if cmd.allowed_roles.is_empty() { + return permissions.administrator() || has_correct_permissions(&cmd, &msg); + } else { + return permissions.administrator() || (has_correct_roles(&cmd, &guild, &member) && has_correct_permissions(cmd, msg)); + } + } + } } + false } /// Posts an embed showing each individual command group and its commands. @@ -100,26 +111,31 @@ pub fn with_embeds(_: &mut Context, if name == with_prefix || name == *command_name { match *command { CommandOrAlias::Command(ref cmd) => { - if !cmd.allowed_roles.is_empty() { - if let Some(guild) = msg.guild() { - let guild = guild.read(); - - if let Some(member) = guild.members.get(&msg.author.id) { - if let Ok(permissions) = member.permissions() { - if !permissions.administrator() && - !has_all_requirements(cmd, &guild, member, &msg) { - break; - } - } - } - } + if has_all_requirements(&cmd, &msg) { + found = Some((command_name, cmd)); } - found = Some((command_name, cmd)); + else { + break; + } }, CommandOrAlias::Alias(ref name) => { - error_embed(&msg.channel_id, &format!("Did you mean \"{}\"?", name)); + let actual_command = group.commands.get(name).unwrap(); - return Ok(()); + match *actual_command { + CommandOrAlias::Command(ref cmd) => { + if has_all_requirements(&cmd, &msg) { + found = Some((name, cmd)); + } + else { + break; + } + }, + + CommandOrAlias::Alias(ref name) => { + let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name)); + return Ok(()); + }, + } }, } } @@ -135,6 +151,7 @@ pub fn with_embeds(_: &mut Context, let _ = msg.channel_id.send_message(|m| { m.embed(|e| { let mut embed = e.colour(Colour::rosewater()).title(command_name); + if let Some(ref desc) = command.desc { embed = embed.description(desc); } @@ -157,6 +174,11 @@ pub fn with_embeds(_: &mut Context, embed = embed.field(|f| f.name("Group").value(&group_name)); } + if !command.aliases.is_empty() { + let aliases = command.aliases.join(", "); + embed = embed.field(|f| f.name("Aliases").value(&aliases)); + } + let available = if command.dm_only { "Only in DM" } else if command.guild_only { @@ -209,22 +231,14 @@ pub fn with_embeds(_: &mut Context, let cmd = &commands[name]; if cmd.help_available { - if let Some(guild) = msg.guild() { - let guild = guild.read(); - - if let Some(member) = guild.members.get(&msg.author.id) { - if let Ok(permissions) = member.permissions() { - if cmd.help_available && - (has_all_requirements(cmd, &guild, member, &msg) || - permissions.administrator()) { - let _ = write!(desc, "`{}`\n", name); - has_commands = true; - } - } - } + + if cmd.help_available && has_all_requirements(&cmd, &msg) { + let _ = write!(desc, "`{}`\n", name); + has_commands = true; } } } + if has_commands { e = e.field(|f| f.name(group_name).value(&desc)); } @@ -275,25 +289,31 @@ pub fn plain(_: &mut Context, if name == with_prefix || name == *command_name { match *command { CommandOrAlias::Command(ref cmd) => { - if !cmd.allowed_roles.is_empty() { - if let Some(guild) = msg.guild() { - let guild = guild.read(); - - if let Some(member) = guild.members.get(&msg.author.id) { - if let Ok(permissions) = member.permissions() { - if !permissions.administrator() && - !has_all_requirements(cmd, &guild, member, &msg) { - break; - } - } - } - } + if has_all_requirements(&cmd, &msg) { + found = Some((command_name, cmd)); + } + else { + break; } - found = Some((command_name, cmd)); }, CommandOrAlias::Alias(ref name) => { - let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name)); - return Ok(()); + let actual_command = group.commands.get(name).unwrap(); + + match *actual_command { + CommandOrAlias::Command(ref cmd) => { + if has_all_requirements(&cmd, &msg) { + found = Some((name, cmd)); + } + else { + break; + } + }, + + CommandOrAlias::Alias(ref name) => { + let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name)); + return Ok(()); + }, + } }, } } @@ -307,6 +327,11 @@ pub fn plain(_: &mut Context, let mut result = format!("**{}**\n", command_name); + if !command.aliases.is_empty() { + let aliases = command.aliases.join("`, `"); + let _ = write!(result, "**Aliases:** `{}`\n", aliases); + } + if let Some(ref desc) = command.desc { let _ = write!(result, "**Description:** {}\n", desc); } @@ -364,17 +389,9 @@ pub fn plain(_: &mut Context, for name in command_names { let cmd = &commands[name]; - if let Some(guild) = msg.guild() { - let guild = guild.read(); - - if let Some(member) = guild.members.get(&msg.author.id) { - if let Ok(permissions) = member.permissions() { - if cmd.help_available && - (permissions.administrator() || has_all_requirements(cmd, &guild, member, &msg)) { - let _ = write!(group_help, "`{}` ", name); - } - } - } + + if cmd.help_available && has_all_requirements(&cmd, &msg) { + let _ = write!(group_help, "`{}` ", name); } } diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs index c3f0d70..6504c6a 100644 --- a/src/framework/standard/mod.rs +++ b/src/framework/standard/mod.rs @@ -417,6 +417,7 @@ impl StandardFramework { } #[allow(too_many_arguments)] + #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] fn should_fail(&mut self, mut context: &mut Context, message: &Message, @@ -514,10 +515,9 @@ impl StandardFramework { if let Some(member) = guild.members.get(&message.author.id) { if let Ok(permissions) = member.permissions() { - if !permissions.administrator() { - if !has_correct_roles(&command, &guild, &member) { - return Some(DispatchError::LackingRole); - } + if !permissions.administrator() + && !has_correct_roles(command, &guild, member) { + return Some(DispatchError::LackingRole); } } } @@ -532,7 +532,7 @@ impl StandardFramework { if all_passed { None } else { - Some(DispatchError::CheckFailed(command.clone())) + Some(DispatchError::CheckFailed(Arc::clone(command))) } } } @@ -875,7 +875,7 @@ impl Framework for StandardFramework { if let Some(&CommandOrAlias::Command(ref command)) = group.commands.get(&to_check) { let before = self.before.clone(); - let command = command.clone(); + let command = Arc::clone(command); let after = self.after.clone(); let groups = self.groups.clone(); @@ -935,7 +935,7 @@ impl Framework for StandardFramework { } #[cfg(feature = "cache")] -pub(crate) fn has_correct_permissions(command: &Command, message: &Message) -> bool { +pub fn has_correct_permissions(command: &Command, message: &Message) -> bool { if !command.required_permissions.is_empty() { if let Some(guild) = message.guild() { let perms = guild @@ -949,7 +949,7 @@ pub(crate) fn has_correct_permissions(command: &Command, message: &Message) -> b } #[cfg(feature = "cache")] -pub(crate) fn has_correct_roles(cmd: &Command, guild: &Guild, member: &Member) -> bool { +pub fn has_correct_roles(cmd: &Command, guild: &Guild, member: &Member) -> bool { cmd.allowed_roles .iter() .flat_map(|r| guild.role_by_name(r)) diff --git a/src/http/ratelimiting.rs b/src/http/ratelimiting.rs index 09c2527..08dc9ee 100644 --- a/src/http/ratelimiting.rs +++ b/src/http/ratelimiting.rs @@ -361,7 +361,7 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result<Response> // - get the global rate; // - sleep if there is 0 remaining // - then, perform the request - let bucket = ROUTES + let bucket = Arc::clone(ROUTES .lock() .entry(route) .or_insert_with(|| { @@ -370,8 +370,7 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result<Response> remaining: i64::MAX, reset: i64::MAX, })) - }) - .clone(); + })); let mut lock = bucket.lock(); lock.pre_hook(&route); diff --git a/src/internal/macros.rs b/src/internal/macros.rs index 4ee43d3..92a21c1 100644 --- a/src/internal/macros.rs +++ b/src/internal/macros.rs @@ -181,3 +181,10 @@ macro_rules! enum_number { } } } + +macro_rules! try_opt { + ($x:expr) => (match $x { + Some(v) => v, + None => return None, + }); +} diff --git a/src/internal/timer.rs b/src/internal/timer.rs index 0bd0d13..6a8bf74 100644 --- a/src/internal/timer.rs +++ b/src/internal/timer.rs @@ -18,11 +18,11 @@ impl Timer { } pub fn await(&mut self) { - let due_time = (self.due.timestamp() * 1000) + self.due.timestamp_subsec_millis() as i64; + let due_time = (self.due.timestamp() * 1000) + i64::from(self.due.timestamp_subsec_millis()); let now_time = { let now = Utc::now(); - (now.timestamp() * 1000) + now.timestamp_subsec_millis() as i64 + (now.timestamp() * 1000) + i64::from(now.timestamp_subsec_millis()) }; if due_time > now_time { @@ -90,15 +90,12 @@ #![allow(doc_markdown, inline_always)] #![warn(enum_glob_use, if_not_else)] -#[allow(unused_imports)] #[macro_use] extern crate bitflags; -#[allow(unused_imports)] #[macro_use] extern crate log; #[macro_use] extern crate serde_derive; -#[allow(unused_imports)] #[macro_use] extern crate serde_json; @@ -107,34 +104,35 @@ extern crate serde_json; extern crate lazy_static; extern crate chrono; -extern crate parking_lot; extern crate serde; -#[cfg(feature = "utils")] +#[cfg(feature = "base64")] extern crate base64; -#[cfg(feature = "voice")] +#[cfg(feature = "byteorder")] extern crate byteorder; -#[cfg(feature = "gateway")] +#[cfg(feature = "flate2")] extern crate flate2; #[cfg(feature = "hyper")] extern crate hyper; #[cfg(feature = "hyper-native-tls")] extern crate hyper_native_tls; -#[cfg(feature = "http")] +#[cfg(feature = "multipart")] extern crate multipart; #[cfg(feature = "native-tls")] extern crate native_tls; -#[cfg(feature = "voice")] +#[cfg(feature = "opus")] extern crate opus; -#[cfg(feature = "voice")] +#[cfg(feature = "parking_lot")] +extern crate parking_lot; +#[cfg(feature = "sodiumoxide")] extern crate sodiumoxide; #[cfg(feature = "threadpool")] extern crate threadpool; -#[cfg(feature = "client")] +#[cfg(feature = "typemap")] extern crate typemap; -#[cfg(feature = "standard_framework")] +#[cfg(feature = "vec_shift")] extern crate vec_shift; -#[cfg(feature = "gateway")] +#[cfg(feature = "evzht9h3nznqzwl")] extern crate evzht9h3nznqzwl as websocket; #[macro_use] diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs index f6efd74..0e939b2 100644 --- a/src/model/channel/message.rs +++ b/src/model/channel/message.rs @@ -370,7 +370,7 @@ impl Message { // Check if the content is over the maximum number of unicode code // points. let count = content.chars().count() as i64; - let diff = count - (constants::MESSAGE_CODE_LIMIT as i64); + let diff = count - i64::from(constants::MESSAGE_CODE_LIMIT); if diff > 0 { Some(diff as u64) @@ -561,7 +561,7 @@ impl Message { if total <= constants::EMBED_MAX_LENGTH as usize { Ok(()) } else { - let overflow = total as u64 - constants::EMBED_MAX_LENGTH as u64; + let overflow = total as u64 - u64::from(constants::EMBED_MAX_LENGTH); Err(Error::Model(ModelError::EmbedTooLarge(overflow))) } diff --git a/src/model/event.rs b/src/model/event.rs index 8d949c7..90a6828 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -57,13 +57,13 @@ impl CacheUpdate for ChannelCreateEvent { fn update(&mut self, cache: &mut Cache) -> Option<Self::Output> { match self.channel { Channel::Group(ref group) => { - let group = group.clone(); + let group = Arc::clone(group); let channel_id = group.with_mut(|writer| { for (recipient_id, recipient) in &mut writer.recipients { cache.update_user_entry(&recipient.read()); - *recipient = cache.users[recipient_id].clone(); + *recipient = Arc::clone(&cache.users[recipient_id]); } writer.channel_id @@ -76,23 +76,23 @@ impl CacheUpdate for ChannelCreateEvent { Channel::Guild(ref channel) => { let (guild_id, channel_id) = channel.with(|channel| (channel.guild_id, channel.id)); - cache.channels.insert(channel_id, channel.clone()); + cache.channels.insert(channel_id, Arc::clone(channel)); cache .guilds .get_mut(&guild_id) .and_then(|guild| { guild - .with_mut(|guild| guild.channels.insert(channel_id, channel.clone())) + .with_mut(|guild| guild.channels.insert(channel_id, Arc::clone(channel))) }) .map(Channel::Guild) }, Channel::Private(ref channel) => { if let Some(channel) = cache.private_channels.get(&channel.with(|c| c.id)) { - return Some(Channel::Private((*channel).clone())); + return Some(Channel::Private(Arc::clone(&(*channel)))); } - let channel = channel.clone(); + let channel = Arc::clone(channel); let id = channel.with_mut(|writer| { let user_id = writer.recipient.with_mut(|user| { @@ -101,16 +101,16 @@ impl CacheUpdate for ChannelCreateEvent { user.id }); - writer.recipient = cache.users[&user_id].clone(); + writer.recipient = Arc::clone(&cache.users[&user_id]); writer.id }); - let ch = cache.private_channels.insert(id, channel.clone()); + let ch = cache.private_channels.insert(id, Arc::clone(&channel)); ch.map(Channel::Private) }, Channel::Category(ref category) => cache .categories - .insert(category.read().id, category.clone()) + .insert(category.read().id, Arc::clone(category)) .map(Channel::Category), } } @@ -211,7 +211,7 @@ impl CacheUpdate for ChannelRecipientAddEvent { fn update(&mut self, cache: &mut Cache) -> Option<()> { cache.update_user_entry(&self.user); - let user = cache.users[&self.user.id].clone(); + let user = Arc::clone(&cache.users[&self.user.id]); cache.groups.get_mut(&self.channel_id).map(|group| { group.write().recipients.insert(self.user.id, user); @@ -260,7 +260,7 @@ impl CacheUpdate for ChannelUpdateEvent { match cache.groups.entry(ch_id) { Entry::Vacant(e) => { - e.insert(group.clone()); + e.insert(Arc::clone(group)); }, Entry::Occupied(mut e) => { let mut dest = e.get_mut().write(); @@ -280,10 +280,10 @@ impl CacheUpdate for ChannelUpdateEvent { Channel::Guild(ref channel) => { let (guild_id, channel_id) = channel.with(|channel| (channel.guild_id, channel.id)); - cache.channels.insert(channel_id, channel.clone()); + cache.channels.insert(channel_id, Arc::clone(channel)); cache.guilds.get_mut(&guild_id).map(|guild| { guild - .with_mut(|g| g.channels.insert(channel_id, channel.clone())) + .with_mut(|g| g.channels.insert(channel_id, Arc::clone(channel))) }); }, Channel::Private(ref channel) => { @@ -341,9 +341,9 @@ impl CacheUpdate for GuildCreateEvent { for (user_id, member) in &mut guild.members { cache.update_user_entry(&member.user.read()); - let user = cache.users[user_id].clone(); + let user = Arc::clone(&cache.users[user_id]); - member.user = user.clone(); + member.user = Arc::clone(&user); } cache.channels.extend(guild.channels.clone()); @@ -431,7 +431,7 @@ impl CacheUpdate for GuildMemberAddEvent { cache.update_user_entry(&self.member.user.read()); // Always safe due to being inserted above. - self.member.user = cache.users[&user_id].clone(); + self.member.user = Arc::clone(&cache.users[&user_id]); cache.guilds.get_mut(&self.guild_id).map(|guild| { guild.with_mut(|guild| { @@ -766,7 +766,7 @@ impl CacheUpdate for PresenceUpdateEvent { if let Some(user) = self.presence.user.as_mut() { cache.update_user_entry(&user.read()); - *user = cache.users[&user_id].clone(); + *user = Arc::clone(&cache.users[&user_id]); } if let Some(guild_id) = self.guild_id { diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs index bca3ecb..02e5afb 100644 --- a/src/model/guild/member.rs +++ b/src/model/guild/member.rs @@ -159,10 +159,7 @@ impl Member { #[cfg(all(feature = "cache", feature = "utils"))] pub fn colour(&self) -> Option<Colour> { let cache = CACHE.read(); - let guild = match cache.guilds.get(&self.guild_id) { - Some(guild) => guild.read(), - None => return None, - }; + let guild = try_opt!(cache.guilds.get(&self.guild_id)).read(); let mut roles = self.roles .iter() @@ -382,25 +379,16 @@ impl Member { /// If role data can not be found for the member, then `None` is returned. #[cfg(feature = "cache")] pub fn roles(&self) -> Option<Vec<Role>> { - CACHE - .read() - .guilds - .values() - .find(|guild| { - guild.read().members.values().any(|m| { - m.user.read().id == self.user.read().id && - m.joined_at == self.joined_at - }) - }) - .map(|guild| { - guild - .read() - .roles - .values() - .filter(|role| self.roles.contains(&role.id)) - .cloned() - .collect() - }) + self + .guild_id + .find() + .map(|g| g + .read() + .roles + .values() + .filter(|role| self.roles.contains(&role.id)) + .cloned() + .collect()) } /// Unbans the [`User`] from the guild. diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index ea6cb17..110ef4e 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -21,6 +21,7 @@ use serde::de::Error as DeError; use serde_json; use super::utils::*; use model::*; +use std; #[cfg(all(feature = "cache", feature = "model"))] use CACHE; @@ -740,21 +741,23 @@ impl Guild { /// Retrieves all [`Member`] that start with a given `String`. /// - /// If the prefix is "zey", following results are possible: - /// - "zey", "zeyla", "zey mei" - /// If 'case_sensitive' is false, the following are not found: - /// - "Zey", "ZEYla", "zeY mei" + /// `sorted` decides whether the best early match of the `prefix` + /// should be the criteria to sort the result. + /// For the `prefix` "zey" and the unsorted result: + /// - "zeya", "zeyaa", "zeyla", "zeyzey", "zeyzeyzey" + /// It would be sorted: + /// - "zeya", "zeyaa", "zeyla", "zeyzey", "zeyzeyzey" /// /// [`Member`]: struct.Member.html - pub fn members_starting_with(&self, prefix: &str, case_sensitive: bool) -> Vec<&Member> { - self.members + pub fn members_starting_with(&self, prefix: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> { + let mut members: Vec<&Member> = self.members .values() .filter(|member| if case_sensitive { member.user.read().name.starts_with(prefix) } else { - starts_with_case_insensitive(&member.user.read().name, &prefix) + starts_with_case_insensitive(&member.user.read().name, prefix) } || member.nick.as_ref() @@ -763,37 +766,214 @@ impl Guild { if case_sensitive { nick.starts_with(prefix) } else { - starts_with_case_insensitive(&nick, &prefix) - })).collect() + starts_with_case_insensitive(nick, prefix) + })).collect(); + + if sorted { + members + .sort_by(|a, b| { + let name_a = match a.nick { + Some(ref nick) => { + if contains_case_insensitive(&a.user.read().name[..], prefix) { + a.user.read().name.clone() + } else { + nick.clone() + } + }, + None => a.user.read().name.clone(), + }; + + let name_b = match b.nick { + Some(ref nick) => { + if contains_case_insensitive(&b.user.read().name[..], prefix) { + b.user.read().name.clone() + } else { + nick.clone() + } + }, + None => b.user.read().name.clone(), + }; + + closest_to_origin(prefix, &name_a[..], &name_b[..]) + }); + members + } else { + members + } } - /// Retrieves all [`Member`] containing a given `String`. + /// Retrieves all [`Member`] containing a given `String` as + /// either username or nick, with a priority on username. /// /// If the substring is "yla", following results are possible: /// - "zeyla", "meiyla", "yladenisyla" /// If 'case_sensitive' is false, the following are not found: /// - "zeYLa", "meiyLa", "LYAdenislyA" /// + /// `sorted` decides whether the best early match of the search-term + /// should be the criteria to sort the result. + /// It will look at the account name first, if that does not fit the + /// search-criteria `substring`, the display-name will be considered. + /// For the `substring` "zey" and the unsorted result: + /// - "azey", "zey", "zeyla", "zeylaa", "zeyzeyzey" + /// It would be sorted: + /// - "zey", "azey", "zeyla", "zeylaa", "zeyzeyzey" + /// + /// **Note**: Due to two fields of a `Member` being candidates for + /// the searched field, setting `sorted` to `true` will result in an overhead, + /// as both fields have to be considered again for sorting. + /// /// [`Member`]: struct.Member.html - pub fn members_containing(&self, substring: &str, case_sensitive: bool) -> Vec<&Member> { - self.members + pub fn members_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> { + let mut members: Vec<&Member> = self.members .values() .filter(|member| if case_sensitive { member.user.read().name.contains(substring) } else { - contains_case_insensitive(&member.user.read().name, &substring) + contains_case_insensitive(&member.user.read().name, substring) } || member.nick.as_ref() - .map_or(false, |nick| + .map_or(false, |nick| { + + if case_sensitive { + nick.contains(substring) + } else { + contains_case_insensitive(nick, substring) + } + })).collect(); + + if sorted { + members + .sort_by(|a, b| { + let name_a = match a.nick { + Some(ref nick) => { + if contains_case_insensitive(&a.user.read().name[..], substring) { + a.user.read().name.clone() + } else { + nick.clone() + } + }, + None => a.user.read().name.clone(), + }; + + let name_b = match b.nick { + Some(ref nick) => { + if contains_case_insensitive(&b.user.read().name[..], substring) { + b.user.read().name.clone() + } else { + nick.clone() + } + }, + None => b.user.read().name.clone(), + }; + + closest_to_origin(substring, &name_a[..], &name_b[..]) + }); + members + } else { + members + } + } - if case_sensitive { - nick.starts_with(substring) - } else { - contains_case_insensitive(&nick, &substring) - })).collect() + /// Retrieves all [`Member`] containing a given `String` in + /// their username. + /// + /// If the substring is "yla", following results are possible: + /// - "zeyla", "meiyla", "yladenisyla" + /// If 'case_sensitive' is false, the following are not found: + /// - "zeYLa", "meiyLa", "LYAdenislyA" + /// + /// `sort` decides whether the best early match of the search-term + /// should be the criteria to sort the result. + /// For the `substring` "zey" and the unsorted result: + /// - "azey", "zey", "zeyla", "zeylaa", "zeyzeyzey" + /// It would be sorted: + /// - "zey", "azey", "zeyla", "zeylaa", "zeyzeyzey" + /// + /// [`Member`]: struct.Member.html + pub fn members_username_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> { + let mut members: Vec<&Member> = self.members + .values() + .filter(|member| { + if case_sensitive { + member.user.read().name.contains(substring) + } else { + contains_case_insensitive(&member.user.read().name, substring) + } + }).collect(); + + if sorted { + members + .sort_by(|a, b| { + let name_a = &a.user.read().name; + let name_b = &b.user.read().name; + closest_to_origin(substring, &name_a[..], &name_b[..]) + }); + members + } else { + members + } + } + + /// Retrieves all [`Member`] containing a given `String` in + /// their nick. + /// + /// If the substring is "yla", following results are possible: + /// - "zeyla", "meiyla", "yladenisyla" + /// If 'case_sensitive' is false, the following are not found: + /// - "zeYLa", "meiyLa", "LYAdenislyA" + /// + /// `sort` decides whether the best early match of the search-term + /// should be the criteria to sort the result. + /// For the `substring` "zey" and the unsorted result: + /// - "azey", "zey", "zeyla", "zeylaa", "zeyzeyzey" + /// It would be sorted: + /// - "zey", "azey", "zeyla", "zeylaa", "zeyzeyzey" + /// + /// **Note**: Instead of panicing, when sorting does not find + /// a nick, the username will be used (this should never happen). + /// + /// [`Member`]: struct.Member.html + pub fn members_nick_containing(&self, substring: &str, case_sensitive: bool, sorted: bool) -> Vec<&Member> { + let mut members: Vec<&Member> = self.members + .values() + .filter(|member| + member.nick.as_ref() + .map_or(false, |nick| { + + if case_sensitive { + nick.contains(substring) + } else { + contains_case_insensitive(nick, substring) + } + })).collect(); + + if sorted { + members + .sort_by(|a, b| { + let name_a = match a.nick { + Some(ref nick) => { + nick.clone() + }, + None => a.user.read().name.clone(), + }; + + let name_b = match b.nick { + Some(ref nick) => { + nick.clone() + }, + None => b.user.read().name.clone(), + }; + + closest_to_origin(substring, &name_a[..], &name_b[..]) + }); + members + } else { + members + } } /// Moves a member to a specific voice channel. @@ -1270,6 +1450,27 @@ fn starts_with_case_insensitive(to_look_at: &str, to_find: &str) -> bool { to_look_at.to_lowercase().starts_with(to_find) } +/// Takes a `&str` as `origin` and tests if either +/// `word_a` or `word_b` is closer. +/// +/// **Note**: Normally `word_a` and `word_b` are +/// expected to contain `origin` as substring. +/// If not, using `closest_to_origin` would sort these +/// the end. +fn closest_to_origin(origin: &str, word_a: &str, word_b: &str) -> std::cmp::Ordering { + let value_a = match word_a.find(origin) { + Some(value) => value + word_a.len(), + None => return std::cmp::Ordering::Greater, + }; + + let value_b = match word_b.find(origin) { + Some(value) => value + word_b.len(), + None => return std::cmp::Ordering::Less, + }; + + value_a.cmp(&value_b) +} + /// Information relating to a guild's widget embed. #[derive(Clone, Copy, Debug, Deserialize)] pub struct GuildEmbed { diff --git a/src/model/permissions.rs b/src/model/permissions.rs index 22599ea..51aafe5 100644 --- a/src/model/permissions.rs +++ b/src/model/permissions.rs @@ -445,7 +445,7 @@ impl<'de> Visitor<'de> for U64Visitor { fn visit_i64<E: DeError>(self, value: i64) -> StdResult<u64, E> { Ok(value as u64) } - fn visit_u32<E: DeError>(self, value: u32) -> StdResult<u64, E> { Ok(value as u64) } + fn visit_u32<E: DeError>(self, value: u32) -> StdResult<u64, E> { Ok(u64::from(value)) } fn visit_u64<E: DeError>(self, value: u64) -> StdResult<u64, E> { Ok(value) } } diff --git a/src/utils/colour.rs b/src/utils/colour.rs index 72df2e9..4e612cc 100644 --- a/src/utils/colour.rs +++ b/src/utils/colour.rs @@ -125,9 +125,9 @@ impl Colour { /// assert_eq!(colour.tuple(), (217, 45, 215)); /// ``` pub fn from_rgb(r: u8, g: u8, b: u8) -> Colour { - let mut uint = r as u32; - uint = (uint << 8) | (g as u32); - uint = (uint << 8) | (b as u32); + let mut uint = u32::from(r); + uint = (uint << 8) | (u32::from(g)); + uint = (uint << 8) | (u32::from(b)); Colour(uint) } diff --git a/src/voice/audio.rs b/src/voice/audio.rs index be3ae60..24a4fcd 100644 --- a/src/voice/audio.rs +++ b/src/voice/audio.rs @@ -1,5 +1,5 @@ pub const HEADER_LEN: usize = 12; -pub const SAMPLE_RATE: u32 = 48000; +pub const SAMPLE_RATE: u32 = 48_000; /// A readable audio source. pub trait AudioSource: Send { diff --git a/src/voice/connection.rs b/src/voice/connection.rs index 47b448c..e507f41 100644 --- a/src/voice/connection.rs +++ b/src/voice/connection.rs @@ -139,7 +139,7 @@ impl Connection { .set_read_timeout(Some(Duration::from_millis(25))); let mutexed_client = Arc::new(Mutex::new(client)); - let thread_items = start_threads(mutexed_client.clone(), &udp)?; + let thread_items = start_threads(Arc::clone(&mutexed_client), &udp)?; info!("[Voice] Connected to: {}", info.endpoint); |