aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/context.rs15
-rw-r--r--src/client/event_store.rs4
-rw-r--r--src/client/gateway/prep.rs50
-rw-r--r--src/client/gateway/shard.rs65
-rw-r--r--src/client/mod.rs6
-rw-r--r--src/client/rest/mod.rs216
-rw-r--r--src/constants.rs106
-rw-r--r--src/ext/cache/mod.rs11
-rw-r--r--src/ext/voice/handler.rs28
-rw-r--r--src/ext/voice/payload.rs59
-rw-r--r--src/internal/prelude.rs5
-rw-r--r--src/lib.rs12
-rw-r--r--src/model/channel/attachment.rs23
-rw-r--r--src/model/channel/channel_id.rs19
-rw-r--r--src/model/channel/embed.rs148
-rw-r--r--src/model/channel/group.rs25
-rw-r--r--src/model/channel/guild_channel.rs95
-rw-r--r--src/model/channel/message.rs125
-rw-r--r--src/model/channel/mod.rs162
-rw-r--r--src/model/channel/private_channel.rs41
-rw-r--r--src/model/channel/reaction.rs19
-rw-r--r--src/model/event.rs772
-rw-r--r--src/model/gateway.rs188
-rw-r--r--src/model/guild/emoji.rs34
-rw-r--r--src/model/guild/feature.rs25
-rw-r--r--src/model/guild/guild_id.rs50
-rw-r--r--src/model/guild/integration.rs26
-rw-r--r--src/model/guild/member.rs41
-rw-r--r--src/model/guild/mod.rs408
-rw-r--r--src/model/guild/partial_guild.rs24
-rw-r--r--src/model/guild/role.rs46
-rw-r--r--src/model/invite.rs83
-rw-r--r--src/model/misc.rs93
-rw-r--r--src/model/mod.rs212
-rw-r--r--src/model/permissions.rs43
-rw-r--r--src/model/user.rs319
-rw-r--r--src/model/utils.rs165
-rw-r--r--src/model/voice.rs36
-rw-r--r--src/model/webhook.rs49
-rw-r--r--src/utils/builder/create_embed.rs105
-rw-r--r--src/utils/builder/create_invite.rs33
-rw-r--r--src/utils/builder/create_message.rs8
-rw-r--r--src/utils/builder/edit_channel.rs41
-rw-r--r--src/utils/builder/edit_guild.rs74
-rw-r--r--src/utils/builder/edit_member.rs44
-rw-r--r--src/utils/builder/edit_profile.rs46
-rw-r--r--src/utils/builder/edit_role.rs76
-rw-r--r--src/utils/builder/execute_webhook.rs39
-rw-r--r--src/utils/colour.rs14
-rw-r--r--src/utils/macros.rs55
-rw-r--r--src/utils/mod.rs13
51 files changed, 2844 insertions, 1552 deletions
diff --git a/src/client/context.rs b/src/client/context.rs
index d208140..27e5ab9 100644
--- a/src/client/context.rs
+++ b/src/client/context.rs
@@ -1,4 +1,3 @@
-use serde_json::builder::ObjectBuilder;
use std::sync::{Arc, Mutex};
use super::gateway::Shard;
use super::rest;
@@ -78,29 +77,27 @@ impl Context {
/// context.edit_profile(|p| p.username("Hakase"));
/// ```
pub fn edit_profile<F: FnOnce(EditProfile) -> EditProfile>(&self, f: F) -> Result<CurrentUser> {
- let mut map = ObjectBuilder::new();
+ let mut map = Map::new();
feature_cache! {{
let cache = CACHE.read().unwrap();
- map = map.insert("avatar", &cache.user.avatar)
- .insert("username", &cache.user.name);
+ map.insert("username".to_owned(), Value::String(cache.user.name.clone()));
if let Some(email) = cache.user.email.as_ref() {
- map = map.insert("email", email);
+ map.insert("email".to_owned(), Value::String(email.clone()));
}
} else {
let user = rest::get_current_user()?;
- map = map.insert("avatar", user.avatar)
- .insert("username", user.name);
+ map.insert("username".to_owned(), Value::String(user.name.clone()));
if let Some(email) = user.email.as_ref() {
- map = map.insert("email", email);
+ map.insert("email".to_owned(), Value::String(email.clone()));
}
}}
- let edited = f(EditProfile(map)).0.build();
+ let edited = f(EditProfile(map)).0;
rest::edit_profile(&edited)
}
diff --git a/src/client/event_store.rs b/src/client/event_store.rs
index 9ec465c..f3767a1 100644
--- a/src/client/event_store.rs
+++ b/src/client/event_store.rs
@@ -1,5 +1,5 @@
use serde_json::Value;
-use std::collections::{BTreeMap, HashMap};
+use std::collections::HashMap;
use std::sync::Arc;
use super::context::Context;
use ::model::event::*;
@@ -85,7 +85,7 @@ pub struct EventStore {
pub on_ready: Option<Arc<Fn(Context, Ready) + Send + Sync + 'static>>,
pub on_resume: Option<Arc<Fn(Context, ResumedEvent) + Send + Sync + 'static>>,
pub on_typing_start: Option<Arc<Fn(Context, TypingStartEvent) + Send + Sync + 'static>>,
- pub on_unknown: Option<Arc<Fn(Context, String, BTreeMap<String, Value>) + Send + Sync + 'static>>,
+ pub on_unknown: Option<Arc<Fn(Context, String, Value) + Send + Sync + 'static>>,
#[cfg(feature="cache")]
pub on_user_update: Option<Arc<Fn(Context, CurrentUser, CurrentUser) + Send + Sync + 'static>>,
#[cfg(not(feature="cache"))]
diff --git a/src/client/gateway/prep.rs b/src/client/gateway/prep.rs
index 97c7dee..4a9b5de 100644
--- a/src/client/gateway/prep.rs
+++ b/src/client/gateway/prep.rs
@@ -1,4 +1,3 @@
-use serde_json::builder::ObjectBuilder;
use serde_json::Value;
use std::sync::mpsc::{
Receiver as MpscReceiver,
@@ -55,32 +54,21 @@ pub fn parse_ready(event: GatewayEvent,
}
pub fn identify(token: &str, shard_info: Option<[u64; 2]>) -> Value {
- ObjectBuilder::new()
- .insert("op", OpCode::Identify.num())
- .insert_object("d", |mut object| {
- object = identify_compression(object)
- .insert("large_threshold", LARGE_THRESHOLD) // max value
- .insert_object("properties", |object| object
- .insert("$browser", "serenity")
- .insert("$device", "serenity")
- .insert("$os", env::consts::OS))
- .insert("token", token)
- .insert("v", constants::GATEWAY_VERSION);
-
- if let Some(shard_info) = shard_info {
- object = object.insert_array("shard", |a| a
- .push(shard_info[0])
- .push(shard_info[1]));
- }
-
- object
- })
- .build()
-}
-
-#[inline(always)]
-pub fn identify_compression(object: ObjectBuilder) -> ObjectBuilder {
- object.insert("compression", !cfg!(feature="debug"))
+ json!({
+ "op": OpCode::Identify.num(),
+ "d": {
+ "compression": !cfg!(feature="debug"),
+ "large_threshold": LARGE_THRESHOLD,
+ "shard": shard_info,
+ "token": token,
+ "v": constants::GATEWAY_VERSION,
+ "properties": {
+ "$browser": "serenity",
+ "$device": "serenity",
+ "$os": env::consts::OS,
+ },
+ },
+ })
}
pub fn build_gateway_url(base: &str) -> Result<RequestUrl> {
@@ -125,10 +113,10 @@ pub fn keepalive(interval: u64,
if time::get_time() >= next_tick {
next_tick = next_tick + base_interval;
- let map = ObjectBuilder::new()
- .insert("d", last_sequence)
- .insert("op", OpCode::Heartbeat.num())
- .build();
+ let map = json!({
+ "d": last_sequence,
+ "op": OpCode::Heartbeat.num(),
+ });
trace!("Sending heartbeat d: {}", last_sequence);
diff --git a/src/client/gateway/shard.rs b/src/client/gateway/shard.rs
index 627ba52..4706397 100644
--- a/src/client/gateway/shard.rs
+++ b/src/client/gateway/shard.rs
@@ -1,4 +1,3 @@
-use serde_json::builder::ObjectBuilder;
use std::io::Write;
use std::net::Shutdown;
use std::sync::mpsc::{self, Sender as MpscSender};
@@ -304,10 +303,10 @@ impl Shard {
};
}
- let map = ObjectBuilder::new()
- .insert("d", Value::Null)
- .insert("op", OpCode::Heartbeat.num())
- .build();
+ let map = json!({
+ "d": Value::Null,
+ "op": OpCode::Heartbeat.num(),
+ });
let status = GatewayStatus::SendMessage(map);
let _ = self.keepalive_channel.send(status);
@@ -496,13 +495,14 @@ impl Shard {
/// If the `cache` feature is enabled, the cache will automatically be
/// updated with member chunks.
pub fn chunk_guilds(&self, guild_ids: &[GuildId], limit: Option<u16>, query: Option<&str>) {
- let msg = ObjectBuilder::new()
- .insert("op", OpCode::GetGuildMembers.num())
- .insert_object("d", |obj| obj
- .insert_array("guild_id", |a| guild_ids.iter().fold(a, |a, s| a.push(s.0)))
- .insert("limit", limit.unwrap_or(0))
- .insert("query", query.unwrap_or("")))
- .build();
+ let msg = json!({
+ "op": OpCode::GetGuildMembers.num(),
+ "d": {
+ "guild_id": guild_ids.iter().map(|x| x.0).collect::<Vec<u64>>(),
+ "limit": limit.unwrap_or(0),
+ "query": query.unwrap_or(""),
+ },
+ });
let _ = self.keepalive_channel.send(GatewayStatus::SendMessage(msg));
}
@@ -578,13 +578,14 @@ impl Shard {
let (mut sender, mut receiver) = response.begin().split();
- sender.send_json(&ObjectBuilder::new()
- .insert_object("d", |o| o
- .insert("session_id", session_id)
- .insert("seq", self.seq)
- .insert("token", &self.token))
- .insert("op", OpCode::Resume.num())
- .build())?;
+ sender.send_json(&json!({
+ "op": OpCode::Resume.num(),
+ "d": {
+ "session_id": session_id,
+ "seq": self.seq,
+ "token": self.token,
+ },
+ }))?;
// Note to self when this gets accepted in a decade:
// https://github.com/rust-lang/rfcs/issues/961
@@ -629,21 +630,17 @@ impl Shard {
let (ref game, status, afk) = self.current_presence;
let now = time::get_time().sec as u64;
- let msg = ObjectBuilder::new()
- .insert("op", OpCode::StatusUpdate.num())
- .insert_object("d", move |mut object| {
- object = object.insert("afk", afk)
- .insert("since", now)
- .insert("status", status.name());
-
- match game.as_ref() {
- Some(game) => {
- object.insert_object("game", move |o| o.insert("name", &game.name))
- },
- None => object.insert("game", Value::Null),
- }
- })
- .build();
+ let msg = json!({
+ "op": OpCode::StatusUpdate.num(),
+ "d": {
+ "afk": afk,
+ "since": now,
+ "status": status.name(),
+ "game": game.as_ref().map(|x| json!({
+ "name": x.name,
+ })),
+ },
+ });
let _ = self.keepalive_channel.send(GatewayStatus::SendMessage(msg));
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 8c073b1..50e0451 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -34,7 +34,7 @@ pub use self::error::Error as ClientError;
use self::dispatch::dispatch;
use self::event_store::EventStore;
use self::gateway::Shard;
-use std::collections::{BTreeMap, HashMap};
+use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use std::time::Duration;
use std::{mem, thread};
@@ -42,7 +42,7 @@ use typemap::ShareMap;
use websocket::client::Receiver;
use websocket::result::WebSocketError;
use websocket::stream::WebSocketStream;
-use ::internal::prelude::{Error, Result, Value};
+use ::internal::prelude::*;
use ::internal::ws_impl::ReceiverExt;
use ::model::event::*;
use ::model::*;
@@ -637,7 +637,7 @@ impl Client {
///
/// [`Unknown`]: ../model/event/enum.Event.html#variant.Unknown
pub fn on_unknown<F>(&mut self, handler: F)
- where F: Fn(Context, String, BTreeMap<String, Value>) + Send + Sync + 'static {
+ where F: Fn(Context, String, Value) + Send + Sync + 'static {
self.event_store.write()
.unwrap()
.on_unknown = Some(Arc::new(handler));
diff --git a/src/client/rest/mod.rs b/src/client/rest/mod.rs
index 8757114..9cd57fa 100644
--- a/src/client/rest/mod.rs
+++ b/src/client/rest/mod.rs
@@ -38,7 +38,6 @@ use hyper::method::Method;
use hyper::{Error as HyperError, Result as HyperResult, Url, header};
use multipart::client::Multipart;
use self::ratelimiting::Route;
-use serde_json::builder::ObjectBuilder;
use serde_json;
use std::collections::BTreeMap;
use std::default::Default;
@@ -48,7 +47,6 @@ use std::sync::{Arc, Mutex};
use ::constants;
use ::internal::prelude::*;
use ::model::*;
-use ::utils::{decode_array, into_array};
/// An method used for ratelimiting special routes.
///
@@ -168,7 +166,7 @@ pub fn create_channel(guild_id: u64, map: &Value) -> Result<GuildChannel> {
"/guilds/{}/channels",
guild_id);
- GuildChannel::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, GuildChannel>(response).map_err(From::from)
}
/// Creates an emoji in the given [`Guild`] with the given data.
@@ -188,7 +186,7 @@ pub fn create_emoji(guild_id: u64, map: &Value) -> Result<Emoji> {
"/guilds/{}/emojis",
guild_id);
- Emoji::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Emoji>(response).map_err(From::from)
}
/// Creates a guild with the data provided.
@@ -230,7 +228,7 @@ pub fn create_guild(map: &Value) -> Result<PartialGuild> {
let body = map.to_string();
let response = request!(Route::Guilds, post(body), "/guilds");
- PartialGuild::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, PartialGuild>(response).map_err(From::from)
}
/// Creates an [`Integration`] for a [`Guild`].
@@ -265,14 +263,14 @@ pub fn create_guild_integration(guild_id: u64, integration_id: u64, map: &Value)
/// [`RichInvite`]: ../../model/struct.RichInvite.html
/// [Create Invite]: ../../model/permissions/constant.CREATE_INVITE.html
/// [docs]: https://discordapp.com/developers/docs/resources/channel#create-channel-invite
-pub fn create_invite(channel_id: u64, map: &Value) -> Result<RichInvite> {
- let body = map.to_string();
+pub fn create_invite(channel_id: u64, map: &JsonMap) -> Result<RichInvite> {
+ let body = serde_json::to_string(map)?;
let response = request!(Route::ChannelsIdInvites(channel_id),
post(body),
"/channels/{}/invites",
channel_id);
- RichInvite::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, RichInvite>(response).map_err(From::from)
}
/// Creates a permission override for a member or a role in a channel.
@@ -293,7 +291,7 @@ pub fn create_private_channel(map: &Value) -> Result<PrivateChannel> {
post(body),
"/users/@me/channels");
- PrivateChannel::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, PrivateChannel>(response).map_err(From::from)
}
/// Reacts to a message.
@@ -310,14 +308,14 @@ pub fn create_reaction(channel_id: u64,
}
/// Creates a role.
-pub fn create_role(guild_id: u64, map: &Value) -> Result<Role> {
- let body = map.to_string();
+pub fn create_role(guild_id: u64, map: &JsonMap) -> Result<Role> {
+ let body = serde_json::to_string(map)?;
let response = request!(Route::GuildsIdRoles(guild_id),
post(body),
"/guilds/{}/roles",
guild_id);
- Role::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Role>(response).map_err(From::from)
}
/// Creates a webhook for the given [channel][`GuildChannel`]'s Id, passing in
@@ -357,7 +355,7 @@ pub fn create_webhook(channel_id: u64, map: &Value) -> Result<Webhook> {
"/channels/{}/webhooks",
channel_id);
- Webhook::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Webhook>(response).map_err(From::from)
}
/// Deletes a private channel or a channel in a guild.
@@ -367,7 +365,7 @@ pub fn delete_channel(channel_id: u64) -> Result<Channel> {
"/channels/{}",
channel_id);
- Channel::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Channel>(response).map_err(From::from)
}
/// Deletes an emoji from a server.
@@ -386,7 +384,7 @@ pub fn delete_guild(guild_id: u64) -> Result<PartialGuild> {
"/guilds/{}",
guild_id);
- PartialGuild::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, PartialGuild>(response).map_err(From::from)
}
/// Remvoes an integration from a guild.
@@ -402,7 +400,7 @@ pub fn delete_guild_integration(guild_id: u64, integration_id: u64) -> Result<()
pub fn delete_invite(code: &str) -> Result<Invite> {
let response = request!(Route::InvitesCode, delete, "/invites/{}", code);
- Invite::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Invite>(response).map_err(From::from)
}
/// Deletes a message if created by us or we have
@@ -537,14 +535,14 @@ pub fn delete_webhook_with_token(webhook_id: u64, token: &str) -> Result<()> {
}
/// Changes channel information.
-pub fn edit_channel(channel_id: u64, map: &Value) -> Result<GuildChannel> {
- let body = map.to_string();
+pub fn edit_channel(channel_id: u64, map: &JsonMap) -> Result<GuildChannel> {
+ let body = serde_json::to_string(map)?;
let response = request!(Route::ChannelsId(channel_id),
patch(body),
"/channels/{}",
channel_id);
- GuildChannel::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, GuildChannel>(response).map_err(From::from)
}
/// Changes emoji information.
@@ -556,18 +554,18 @@ pub fn edit_emoji(guild_id: u64, emoji_id: u64, map: &Value) -> Result<Emoji> {
guild_id,
emoji_id);
- Emoji::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Emoji>(response).map_err(From::from)
}
/// Changes guild information.
-pub fn edit_guild(guild_id: u64, map: &Value) -> Result<PartialGuild> {
- let body = map.to_string();
+pub fn edit_guild(guild_id: u64, map: &JsonMap) -> Result<PartialGuild> {
+ let body = serde_json::to_string(map)?;
let response = request!(Route::GuildsId(guild_id),
patch(body),
"/guilds/{}",
guild_id);
- PartialGuild::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, PartialGuild>(response).map_err(From::from)
}
/// Edits a [`Guild`]'s embed setting.
@@ -580,12 +578,12 @@ pub fn edit_guild_embed(guild_id: u64, map: &Value) -> Result<GuildEmbed> {
"/guilds/{}/embed",
guild_id);
- GuildEmbed::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, GuildEmbed>(response).map_err(From::from)
}
/// Does specific actions to a member.
-pub fn edit_member(guild_id: u64, user_id: u64, map: &Value) -> Result<()> {
- let body = map.to_string();
+pub fn edit_member(guild_id: u64, user_id: u64, map: &JsonMap) -> Result<()> {
+ let body = serde_json::to_string(map)?;
verify(204, request!(Route::GuildsIdMembersId(guild_id),
patch(body),
@@ -605,7 +603,7 @@ pub fn edit_message(channel_id: u64, message_id: u64, map: &Value) -> Result<Mes
channel_id,
message_id);
- Message::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Message>(response).map_err(From::from)
}
/// Edits the current user's nickname for the provided [`Guild`] via its Id.
@@ -614,7 +612,9 @@ pub fn edit_message(channel_id: u64, message_id: u64, map: &Value) -> Result<Mes
///
/// [`Guild`]: ../../model/struct.Guild.html
pub fn edit_nickname(guild_id: u64, new_nickname: Option<&str>) -> Result<()> {
- let map = ObjectBuilder::new().insert("nick", new_nickname).build();
+ let map = json!({
+ "nick": new_nickname
+ });
let body = map.to_string();
let response = request!(Route::GuildsIdMembersMeNick(guild_id),
patch(body),
@@ -636,32 +636,33 @@ pub fn edit_nickname(guild_id: u64, new_nickname: Option<&str>) -> Result<()> {
/// **Note**: this token change may cause requests made between the actual token
/// change and when the token is internally changed to be invalid requests, as
/// the token may be outdated.
-pub fn edit_profile(map: &Value) -> Result<CurrentUser> {
- let body = map.to_string();
+pub fn edit_profile(map: &JsonMap) -> Result<CurrentUser> {
+ let body = serde_json::to_string(map)?;
let response = request!(Route::UsersMe, patch(body), "/users/@me");
- let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
+ let mut value = serde_json::from_reader::<HyperResponse, Value>(response)?;
- if !TOKEN.lock().unwrap().starts_with("Bot ") {
- if let Some(Value::String(token)) = map.remove("token") {
- set_token(&token);
+ if let Some(map) = value.as_object_mut() {
+ if !TOKEN.lock().unwrap().starts_with("Bot ") {
+ if let Some(Value::String(token)) = map.remove("token") {
+ set_token(&token);
+ }
}
}
- CurrentUser::decode(Value::Object(map))
+ serde_json::from_value::<CurrentUser>(value).map_err(From::from)
}
/// Changes a role in a guild.
-pub fn edit_role(guild_id: u64, role_id: u64, map: &Value)
- -> Result<Role> {
- let body = map.to_string();
+pub fn edit_role(guild_id: u64, role_id: u64, map: &JsonMap) -> Result<Role> {
+ let body = serde_json::to_string(map)?;
let response = request!(Route::GuildsIdRolesId(guild_id),
patch(body),
"/guilds/{}/roles/{}",
guild_id,
role_id);
- Role::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Role>(response).map_err(From::from)
}
/// Edits a the webhook with the given data.
@@ -710,7 +711,7 @@ pub fn edit_webhook(webhook_id: u64, map: &Value) -> Result<Webhook> {
"/webhooks/{}",
webhook_id);
- Webhook::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Webhook>(response).map_err(From::from)
}
/// Edits the webhook with the given data.
@@ -739,15 +740,15 @@ pub fn edit_webhook(webhook_id: u64, map: &Value) -> Result<Webhook> {
/// ```
///
/// [`edit_webhook`]: fn.edit_webhook.html
-pub fn edit_webhook_with_token(webhook_id: u64, token: &str, map: &Value) -> Result<Webhook> {
- let body = map.to_string();
+pub fn edit_webhook_with_token(webhook_id: u64, token: &str, map: &JsonMap) -> Result<Webhook> {
+ let body = serde_json::to_string(map)?;
let client = HyperClient::new();
let response = retry(|| client
.patch(&format!(api!("/webhooks/{}/{}"), webhook_id, token))
.body(&body))
.map_err(Error::Hyper)?;
- Webhook::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Webhook>(response).map_err(From::from)
}
/// Executes a webhook, posting a [`Message`] in the webhook's associated
@@ -804,15 +805,15 @@ pub fn edit_webhook_with_token(webhook_id: u64, token: &str, map: &Value) -> Res
/// [`Channel`]: ../../model/enum.Channel.html
/// [`Message`]: ../../model/struct.Message.html
/// [Discord docs]: https://discordapp.com/developers/docs/resources/webhook#querystring-params
-pub fn execute_webhook(webhook_id: u64, token: &str, map: &Value) -> Result<Message> {
- let body = map.to_string();
+pub fn execute_webhook(webhook_id: u64, token: &str, map: &JsonMap) -> Result<Message> {
+ let body = serde_json::to_string(map)?;
let client = HyperClient::new();
let response = retry(|| client
.post(&format!(api!("/webhooks/{}/{}"), webhook_id, token))
.body(&body))
.map_err(Error::Hyper)?;
- Message::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Message>(response).map_err(From::from)
}
/// Gets the active maintenances from Discord's Status API.
@@ -826,7 +827,7 @@ pub fn get_active_maintenances() -> Result<Vec<Maintenance>> {
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
match map.remove("scheduled_maintenances") {
- Some(v) => decode_array(v, Maintenance::decode),
+ Some(v) => serde_json::from_value::<Vec<Maintenance>>(v).map_err(From::from),
None => Ok(vec![]),
}
}
@@ -837,7 +838,7 @@ pub fn get_active_maintenances() -> Result<Vec<Maintenance>> {
pub fn get_application_info(id: u64) -> Result<ApplicationInfo> {
let response = request!(Route::None, get, "/oauth2/applications/{}", id);
- ApplicationInfo::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, ApplicationInfo>(response).map_err(From::from)
}
/// Gets all oauth2 applications we've made.
@@ -845,9 +846,8 @@ pub fn get_application_info(id: u64) -> Result<ApplicationInfo> {
/// **Note**: Only user accounts may use this endpoint.
pub fn get_applications() -> Result<Vec<ApplicationInfo>> {
let response = request!(Route::None, get, "/oauth2/applications");
- let decoded = serde_json::from_reader(response)?;
- decode_array(decoded, ApplicationInfo::decode)
+ serde_json::from_reader::<HyperResponse, Vec<ApplicationInfo>>(response).map_err(From::from)
}
/// Gets all the users that are banned in specific guild.
@@ -857,14 +857,14 @@ pub fn get_bans(guild_id: u64) -> Result<Vec<Ban>> {
"/guilds/{}/bans",
guild_id);
- decode_array(serde_json::from_reader(response)?, Ban::decode)
+ serde_json::from_reader::<HyperResponse, Vec<Ban>>(response).map_err(From::from)
}
/// Gets current bot gateway.
pub fn get_bot_gateway() -> Result<BotGateway> {
let response = request!(Route::GatewayBot, get, "/gateway/bot");
- BotGateway::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, BotGateway>(response).map_err(From::from)
}
/// Gets all invites for a channel.
@@ -874,8 +874,7 @@ pub fn get_channel_invites(channel_id: u64) -> Result<Vec<RichInvite>> {
"/channels/{}/invites",
channel_id);
- decode_array(serde_json::from_reader(response)?,
- RichInvite::decode)
+ serde_json::from_reader::<HyperResponse, Vec<RichInvite>>(response).map_err(From::from)
}
/// Retrieves the webhooks for the given [channel][`GuildChannel`]'s Id.
@@ -902,7 +901,7 @@ pub fn get_channel_webhooks(channel_id: u64) -> Result<Vec<Webhook>> {
"/channels/{}/webhooks",
channel_id);
- decode_array(serde_json::from_reader(response)?, Webhook::decode)
+ serde_json::from_reader::<HyperResponse, Vec<Webhook>>(response).map_err(From::from)
}
/// Gets channel information.
@@ -912,7 +911,7 @@ pub fn get_channel(channel_id: u64) -> Result<Channel> {
"/channels/{}",
channel_id);
- Channel::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Channel>(response).map_err(From::from)
}
/// Gets all channels in a guild.
@@ -922,8 +921,7 @@ pub fn get_channels(guild_id: u64) -> Result<Vec<GuildChannel>> {
"/guilds/{}/channels",
guild_id);
- decode_array(serde_json::from_reader(response)?,
- GuildChannel::decode)
+ serde_json::from_reader::<HyperResponse, Vec<GuildChannel>>(response).map_err(From::from)
}
/// Gets information about the current application.
@@ -932,21 +930,21 @@ pub fn get_channels(guild_id: u64) -> Result<Vec<GuildChannel>> {
pub fn get_current_application_info() -> Result<CurrentApplicationInfo> {
let response = request!(Route::None, get, "/oauth2/applications/@me");
- CurrentApplicationInfo::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, CurrentApplicationInfo>(response).map_err(From::from)
}
/// Gets information about the user we're connected with.
pub fn get_current_user() -> Result<CurrentUser> {
let response = request!(Route::UsersMe, get, "/users/@me");
- CurrentUser::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, CurrentUser>(response).map_err(From::from)
}
/// Gets current gateway.
pub fn get_gateway() -> Result<Gateway> {
let response = request!(Route::Gateway, get, "/gateway");
- Gateway::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Gateway>(response).map_err(From::from)
}
/// Gets information about an emoji.
@@ -957,7 +955,7 @@ pub fn get_emoji(guild_id: u64, emoji_id: u64) -> Result<Emoji> {
guild_id,
emoji_id);
- Emoji::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Emoji>(response).map_err(From::from)
}
/// Gets all emojis in a guild.
@@ -967,7 +965,7 @@ pub fn get_emojis(guild_id: u64) -> Result<Vec<Emoji>> {
"/guilds/{}/emojis",
guild_id);
- decode_array(serde_json::from_reader(response)?, Emoji::decode)
+ serde_json::from_reader::<HyperResponse, Vec<Emoji>>(response).map_err(From::from)
}
/// Gets guild information.
@@ -977,7 +975,7 @@ pub fn get_guild(guild_id: u64) -> Result<PartialGuild> {
"/guilds/{}",
guild_id);
- PartialGuild::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, PartialGuild>(response).map_err(From::from)
}
/// Gets a guild embed information.
@@ -987,7 +985,7 @@ pub fn get_guild_embed(guild_id: u64) -> Result<GuildEmbed> {
"/guilds/{}/embeds",
guild_id);
- GuildEmbed::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, GuildEmbed>(response).map_err(From::from)
}
/// Gets integrations that a guild has.
@@ -997,7 +995,7 @@ pub fn get_guild_integrations(guild_id: u64) -> Result<Vec<Integration>> {
"/guilds/{}/integrations",
guild_id);
- decode_array(serde_json::from_reader(response)?, Integration::decode)
+ serde_json::from_reader::<HyperResponse, Vec<Integration>>(response).map_err(From::from)
}
/// Gets all invites to a guild.
@@ -1007,7 +1005,7 @@ pub fn get_guild_invites(guild_id: u64) -> Result<Vec<RichInvite>> {
"/guilds/{}/invites",
guild_id);
- decode_array(serde_json::from_reader(response)?, RichInvite::decode)
+ serde_json::from_reader::<HyperResponse, Vec<RichInvite>>(response).map_err(From::from)
}
/// Gets the members of a guild. Optionally pass a `limit` and the Id of the
@@ -1021,10 +1019,19 @@ pub fn get_guild_members(guild_id: u64, limit: Option<u64>, after: Option<u64>)
limit.unwrap_or(500),
after.unwrap_or(0));
- into_array(serde_json::from_reader(response)?)
- .and_then(|x| x.into_iter()
- .map(|v| Member::decode_guild(GuildId(guild_id), v))
- .collect())
+ let mut v = serde_json::from_reader::<HyperResponse, Value>(response)?;
+
+ if let Some(values) = v.as_array_mut() {
+ let num = Value::Number(Number::from(guild_id));
+
+ for value in values {
+ if let Some(element) = value.as_object_mut() {
+ element.insert("guild_id".to_owned(), num.clone());
+ }
+ }
+ }
+
+ serde_json::from_value::<Vec<Member>>(v).map_err(From::from)
}
/// Gets the amount of users that can be pruned.
@@ -1035,7 +1042,7 @@ pub fn get_guild_prune_count(guild_id: u64, map: &Value) -> Result<GuildPrune> {
"/guilds/{}/prune",
guild_id);
- GuildPrune::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, GuildPrune>(response).map_err(From::from)
}
/// Gets regions that a guild can use. If a guild has [`Feature::VipRegions`]
@@ -1048,7 +1055,7 @@ pub fn get_guild_regions(guild_id: u64) -> Result<Vec<VoiceRegion>> {
"/guilds/{}/regions",
guild_id);
- decode_array(serde_json::from_reader(response)?, VoiceRegion::decode)
+ serde_json::from_reader::<HyperResponse, Vec<VoiceRegion>>(response).map_err(From::from)
}
/// Retrieves a list of roles in a [`Guild`].
@@ -1060,7 +1067,7 @@ pub fn get_guild_roles(guild_id: u64) -> Result<Vec<Role>> {
"/guilds/{}/roles",
guild_id);
- decode_array(serde_json::from_reader(response)?, Role::decode)
+ serde_json::from_reader::<HyperResponse, Vec<Role>>(response).map_err(From::from)
}
/// Retrieves the webhooks for the given [guild][`Guild`]'s Id.
@@ -1087,7 +1094,7 @@ pub fn get_guild_webhooks(guild_id: u64) -> Result<Vec<Webhook>> {
"/guilds/{}/webhooks",
guild_id);
- decode_array(serde_json::from_reader(response)?, Webhook::decode)
+ serde_json::from_reader::<HyperResponse, Vec<Webhook>>(response).map_err(From::from)
}
/// Gets a paginated list of the current user's guilds.
@@ -1124,7 +1131,7 @@ pub fn get_guilds(target: &GuildPagination, limit: u64) -> Result<Vec<GuildInfo>
let response = request!(Route::UsersMeGuilds, get, "{}", uri);
- decode_array(serde_json::from_reader(response)?, GuildInfo::decode)
+ serde_json::from_reader::<HyperResponse, Vec<GuildInfo>>(response).map_err(From::from)
}
/// Gets information about a specific invite.
@@ -1132,7 +1139,7 @@ pub fn get_invite(code: &str) -> Result<Invite> {
let invite = ::utils::parse_invite(code);
let response = request!(Route::InvitesCode, get, "/invites/{}", invite);
- Invite::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Invite>(response).map_err(From::from)
}
/// Gets member of a guild.
@@ -1143,7 +1150,13 @@ pub fn get_member(guild_id: u64, user_id: u64) -> Result<Member> {
guild_id,
user_id);
- Member::decode_guild(GuildId(guild_id), serde_json::from_reader(response)?)
+ let mut v = serde_json::from_reader::<HyperResponse, Value>(response)?;
+
+ if let Some(map) = v.as_object_mut() {
+ map.insert("guild_id".to_owned(), Value::Number(Number::from(guild_id)));
+ }
+
+ serde_json::from_value::<Member>(v).map_err(From::from)
}
/// Gets a message by an Id, bots only.
@@ -1154,7 +1167,7 @@ pub fn get_message(channel_id: u64, message_id: u64) -> Result<Message> {
channel_id,
message_id);
- Message::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Message>(response).map_err(From::from)
}
/// Gets X messages from a channel.
@@ -1167,7 +1180,7 @@ pub fn get_messages(channel_id: u64, query: &str)
let response = request(Route::ChannelsIdMessages(channel_id),
|| client.get(&url))?;
- decode_array(serde_json::from_reader(response)?, Message::decode)
+ serde_json::from_reader::<HyperResponse, Vec<Message>>(response).map_err(From::from)
}
/// Gets all pins of a channel.
@@ -1177,7 +1190,7 @@ pub fn get_pins(channel_id: u64) -> Result<Vec<Message>> {
"/channels/{}/pins",
channel_id);
- decode_array(serde_json::from_reader(response)?, Message::decode)
+ serde_json::from_reader::<HyperResponse, Vec<Message>>(response).map_err(From::from)
}
/// Gets user Ids based on their reaction to a message. This endpoint is dumb.
@@ -1202,7 +1215,7 @@ pub fn get_reaction_users(channel_id: u64,
"{}",
uri);
- decode_array(serde_json::from_reader(response)?, User::decode)
+ serde_json::from_reader::<HyperResponse, Vec<User>>(response).map_err(From::from)
}
/// Gets the current unresolved incidents from Discord's Status API.
@@ -1216,7 +1229,7 @@ pub fn get_unresolved_incidents() -> Result<Vec<Incident>> {
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
match map.remove("incidents") {
- Some(incidents) => decode_array(incidents, Incident::decode),
+ Some(v) => serde_json::from_value::<Vec<Incident>>(v).map_err(From::from),
None => Ok(vec![]),
}
}
@@ -1232,7 +1245,7 @@ pub fn get_upcoming_maintenances() -> Result<Vec<Maintenance>> {
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
match map.remove("scheduled_maintenances") {
- Some(v) => decode_array(v, Maintenance::decode),
+ Some(v) => serde_json::from_value::<Vec<Maintenance>>(v).map_err(From::from),
None => Ok(vec![]),
}
}
@@ -1241,21 +1254,21 @@ pub fn get_upcoming_maintenances() -> Result<Vec<Maintenance>> {
pub fn get_user(user_id: u64) -> Result<User> {
let response = request!(Route::UsersId, get, "/users/{}", user_id);
- User::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, User>(response).map_err(From::from)
}
/// Gets our DM channels.
pub fn get_user_dm_channels() -> Result<Vec<PrivateChannel>> {
let response = request!(Route::UsersMeChannels, get, "/users/@me/channels");
- decode_array(serde_json::from_reader(response)?, PrivateChannel::decode)
+ serde_json::from_reader::<HyperResponse, Vec<PrivateChannel>>(response).map_err(From::from)
}
/// Gets all voice regions.
pub fn get_voice_regions() -> Result<Vec<VoiceRegion>> {
let response = request!(Route::VoiceRegions, get, "/voice/regions");
- decode_array(serde_json::from_reader(response)?, VoiceRegion::decode)
+ serde_json::from_reader::<HyperResponse, Vec<VoiceRegion>>(response).map_err(From::from)
}
/// Retrieves a webhook given its Id.
@@ -1278,7 +1291,7 @@ pub fn get_voice_regions() -> Result<Vec<VoiceRegion>> {
pub fn get_webhook(webhook_id: u64) -> Result<Webhook> {
let response = request!(Route::WebhooksId, get, "/webhooks/{}", webhook_id);
- Webhook::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Webhook>(response).map_err(From::from)
}
/// Retrieves a webhook given its Id and unique token.
@@ -1304,7 +1317,7 @@ pub fn get_webhook_with_token(webhook_id: u64, token: &str) -> Result<Webhook> {
.get(&format!(api!("/webhooks/{}/{}"), webhook_id, token)))
.map_err(Error::Hyper)?;
- Webhook::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Webhook>(response).map_err(From::from)
}
/// Kicks a member from a guild.
@@ -1323,7 +1336,7 @@ pub fn leave_group(guild_id: u64) -> Result<Group> {
"/channels/{}",
guild_id);
- Group::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Group>(response).map_err(From::from)
}
/// Leaves a guild.
@@ -1333,7 +1346,7 @@ pub fn leave_guild(guild_id: u64) -> Result<PartialGuild> {
"/users/@me/guilds/{}",
guild_id);
- PartialGuild::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, PartialGuild>(response).map_err(From::from)
}
/// Deletes a user from group DM.
@@ -1346,11 +1359,8 @@ pub fn remove_group_recipient(group_id: u64, user_id: u64) -> Result<()> {
}
/// Sends a file to a channel.
-pub fn send_file<R: Read>(channel_id: u64,
- mut file: R,
- filename: &str,
- map: BTreeMap<String, Value>)
- -> Result<Message> {
+pub fn send_file<R: Read>(channel_id: u64, mut file: R, filename: &str, map: JsonMap)
+ -> Result<Message> {
let uri = format!(api!("/channels/{}/messages"), channel_id);
let url = match Url::parse(&uri) {
Ok(url) => url,
@@ -1371,14 +1381,15 @@ pub fn send_file<R: Read>(channel_id: u64,
let _ = match v {
Value::Bool(false) => request.write_text(&k, "false")?,
Value::Bool(true) => request.write_text(&k, "true")?,
- Value::I64(inner) => request.write_text(&k, inner.to_string())?,
- Value::U64(inner) => request.write_text(&k, inner.to_string())?,
+ Value::Number(inner) => request.write_text(&k, inner.to_string())?,
Value::String(inner) => request.write_text(&k, inner)?,
_ => continue,
};
}
- Message::decode(serde_json::from_reader(request.send()?)?)
+ let response = request.send()?;
+
+ serde_json::from_reader::<HyperResponse, Message>(response).map_err(From::from)
}
/// Sends a message to a channel.
@@ -1389,7 +1400,7 @@ pub fn send_message(channel_id: u64, map: &Value) -> Result<Message> {
"/channels/{}/messages",
channel_id);
- Message::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, Message>(response).map_err(From::from)
}
/// Pins a message in a channel.
@@ -1436,12 +1447,11 @@ pub fn start_guild_prune(guild_id: u64, map: &Value) -> Result<GuildPrune> {
"/guilds/{}/prune",
guild_id);
- GuildPrune::decode(serde_json::from_reader(response)?)
+ serde_json::from_reader::<HyperResponse, GuildPrune>(response).map_err(From::from)
}
/// Starts syncing an integration with a guild.
-pub fn start_integration_sync(guild_id: u64, integration_id: u64)
- -> Result<()> {
+pub fn start_integration_sync(guild_id: u64, integration_id: u64) -> Result<()> {
verify(204, request!(Route::GuildsIdIntegrationsIdSync(guild_id),
post,
"/guilds/{}/integrations/{}/sync",
diff --git a/src/constants.rs b/src/constants.rs
index 99327b7..0ec973c 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -1,3 +1,5 @@
+use std::result::Result as StdResult;
+
/// The gateway version used by the library. The gateway URI is retrieved via
/// the REST API.
pub const GATEWAY_VERSION: u8 = 6;
@@ -56,41 +58,37 @@ pub enum ErrorCode {
UnknownUser,
}
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum OpCode {
- Event,
- Heartbeat,
- Identify,
- StatusUpdate,
- VoiceStateUpdate,
- VoiceServerPing,
- Resume,
- Reconnect,
- GetGuildMembers,
- InvalidSession,
- Hello,
- HeartbeatAck,
-}
-
-impl OpCode {
- pub fn from_num(num: u64) -> Option<Self> {
- match num {
- 0 => Some(OpCode::Event),
- 1 => Some(OpCode::Heartbeat),
- 2 => Some(OpCode::Identify),
- 3 => Some(OpCode::StatusUpdate),
- 4 => Some(OpCode::VoiceStateUpdate),
- 5 => Some(OpCode::VoiceServerPing),
- 6 => Some(OpCode::Resume),
- 7 => Some(OpCode::Reconnect),
- 8 => Some(OpCode::GetGuildMembers),
- 9 => Some(OpCode::InvalidSession),
- 10 => Some(OpCode::Hello),
- 11 => Some(OpCode::HeartbeatAck),
- _ => None,
- }
+enum_number!(
+ /// Enum to map gateway opcodes.
+ OpCode {
+ /// Dispatches an event.
+ Event = 0,
+ /// Used for ping checking.
+ Heartbeat = 1,
+ /// Used for client handshake.
+ Identify = 2,
+ /// Used to update the client status.
+ StatusUpdate = 3,
+ /// Used to join/move/leave voice channels.
+ VoiceStateUpdate = 4,
+ /// Used for voice ping checking.
+ VoiceServerPing = 5,
+ /// Used to resume a closed connection.
+ Resume = 6,
+ /// Used to tell clients to reconnect to the gateway.
+ Reconnect = 7,
+ /// Used to request guild members.
+ GetGuildMembers = 8,
+ /// Used to notify clients that they have an invalid session Id.
+ InvalidSession = 9,
+ /// Sent immediately after connection, contains heartbeat + server info.
+ Hello = 10,
+ /// Sent immediately following a client heartbeat that was received.
+ HeartbeatAck = 11,
}
+);
+impl OpCode {
pub fn num(&self) -> u64 {
match *self {
OpCode::Event => 0,
@@ -109,31 +107,27 @@ impl OpCode {
}
}
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum VoiceOpCode {
- Identify,
- Heartbeat,
- Hello,
- KeepAlive,
- SelectProtocol,
- SessionDescription,
- Speaking,
-}
-
-impl VoiceOpCode {
- pub fn from_num(num: u64) -> Option<Self> {
- match num {
- 0 => Some(VoiceOpCode::Identify),
- 1 => Some(VoiceOpCode::SelectProtocol),
- 2 => Some(VoiceOpCode::Hello),
- 3 => Some(VoiceOpCode::KeepAlive),
- 4 => Some(VoiceOpCode::SessionDescription),
- 5 => Some(VoiceOpCode::Speaking),
- 8 => Some(VoiceOpCode::Heartbeat),
- _ => None,
- }
+enum_number!(
+ /// Enum to map voice opcodes.
+ VoiceOpCode {
+ /// Used to begin a voice websocket connection.
+ Identify = 0,
+ /// Used to select the voice protocol.
+ SelectProtocol = 1,
+ /// Used to complete the websocket handshake.
+ Hello = 2,
+ /// Used to keep the websocket connection alive.
+ KeepAlive = 3,
+ /// Used to describe the session.
+ SessionDescription = 4,
+ /// Used to indicate which users are speaking.
+ Speaking = 5,
+ /// Used to heartbeat.
+ Heartbeat = 8,
}
+);
+impl VoiceOpCode {
pub fn num(&self) -> u64 {
match *self {
VoiceOpCode::Identify => 0,
diff --git a/src/ext/cache/mod.rs b/src/ext/cache/mod.rs
index ee26816..84c21a7 100644
--- a/src/ext/cache/mod.rs
+++ b/src/ext/cache/mod.rs
@@ -842,14 +842,15 @@ impl Cache {
for guild in ready.guilds {
match guild {
- PossibleGuild::Offline(guild_id) => {
- self.unavailable_guilds.insert(guild_id);
- self.guilds.remove(&guild_id);
+ GuildStatus::Offline(unavailable) => {
+ self.guilds.remove(&unavailable.id);
+ self.unavailable_guilds.insert(unavailable.id);
},
- PossibleGuild::Online(guild) => {
- self.channels.extend(guild.channels.clone());
+ GuildStatus::OnlineGuild(guild) => {
+ self.unavailable_guilds.remove(&guild.id);
self.guilds.insert(guild.id, Arc::new(RwLock::new(guild)));
},
+ GuildStatus::OnlinePartialGuild(_) => {},
}
}
diff --git a/src/ext/voice/handler.rs b/src/ext/voice/handler.rs
index 60cc6e6..c81a57f 100644
--- a/src/ext/voice/handler.rs
+++ b/src/ext/voice/handler.rs
@@ -1,4 +1,3 @@
-use serde_json::builder::ObjectBuilder;
use std::sync::mpsc::{self, Sender as MpscSender};
use super::{AudioReceiver, AudioSource};
use super::connection_info::ConnectionInfo;
@@ -162,11 +161,11 @@ impl Handler {
// Safe as all of these being present was already checked.
self.send(VoiceStatus::Connect(ConnectionInfo {
- endpoint,
- guild_id,
- session_id,
- token,
- user_id,
+ endpoint: endpoint,
+ guild_id: guild_id,
+ session_id: session_id,
+ token: token,
+ user_id: user_id,
}));
true
@@ -409,14 +408,15 @@ impl Handler {
/// [`standalone`]: #method.standalone
fn update(&self) {
if let Some(ref ws) = self.ws {
- let map = ObjectBuilder::new()
- .insert("op", VoiceOpCode::SessionDescription.num())
- .insert_object("d", |o| o
- .insert("channel_id", self.channel_id.map(|c| c.0))
- .insert("guild_id", self.guild_id.0)
- .insert("self_deaf", self.self_deaf)
- .insert("self_mute", self.self_mute))
- .build();
+ let map = json!({
+ "op": VoiceOpCode::SessionDescription.num(),
+ "d": {
+ "channel_id": self.channel_id.map(|c| c.0),
+ "guild_id": self.guild_id.0,
+ "self_deaf": self.self_deaf,
+ "self_mute": self.self_mute,
+ }
+ });
let _ = ws.send(GatewayStatus::SendMessage(map));
}
diff --git a/src/ext/voice/payload.rs b/src/ext/voice/payload.rs
index 964d6d2..c2e7c0c 100644
--- a/src/ext/voice/payload.rs
+++ b/src/ext/voice/payload.rs
@@ -1,47 +1,50 @@
-use serde_json::builder::ObjectBuilder;
use serde_json::Value;
use super::connection_info::ConnectionInfo;
use ::constants::VoiceOpCode;
#[inline]
pub fn build_identify(info: &ConnectionInfo) -> Value {
- ObjectBuilder::new()
- .insert("op", VoiceOpCode::Identify.num())
- .insert_object("d", |o| o
- .insert("server_id", info.guild_id.0)
- .insert("session_id", &info.session_id)
- .insert("token", &info.token)
- .insert("user_id", info.user_id.0))
- .build()
+ json!({
+ "op": VoiceOpCode::Identify.num(),
+ "d": {
+ "server_id": info.guild_id.0,
+ "session_id": &info.session_id,
+ "token": &info.token,
+ "user_id": info.user_id.0,
+ }
+ })
}
#[inline]
pub fn build_keepalive() -> Value {
- ObjectBuilder::new()
- .insert("op", VoiceOpCode::KeepAlive.num())
- .insert("d", Value::Null)
- .build()
+ json!({
+ "op": VoiceOpCode::KeepAlive.num(),
+ "d": Value::Null,
+ })
}
#[inline]
pub fn build_select_protocol(address: ::std::borrow::Cow<str>, port: u16) -> Value {
- ObjectBuilder::new()
- .insert("op", VoiceOpCode::SelectProtocol.num())
- .insert_object("d", |o| o
- .insert("protocol", "udp")
- .insert_object("data", |o| o
- .insert("address", address)
- .insert("mode", super::CRYPTO_MODE)
- .insert("port", port)))
- .build()
+ json!({
+ "op": VoiceOpCode::SelectProtocol.num(),
+ "d": {
+ "protocol": "udp",
+ "data": {
+ "address": address,
+ "mode": super::CRYPTO_MODE,
+ "port": port,
+ }
+ }
+ })
}
#[inline]
pub fn build_speaking(speaking: bool) -> Value {
- ObjectBuilder::new()
- .insert("op", VoiceOpCode::Speaking.num())
- .insert_object("d", |o| o
- .insert("delay", 0)
- .insert("speaking", speaking))
- .build()
+ json!({
+ "op": VoiceOpCode::Speaking.num(),
+ "d": {
+ "delay": 0,
+ "speaking": speaking,
+ }
+ })
}
diff --git a/src/internal/prelude.rs b/src/internal/prelude.rs
index 9b6b993..46cd337 100644
--- a/src/internal/prelude.rs
+++ b/src/internal/prelude.rs
@@ -4,6 +4,9 @@
//! These are not publicly re-exported to the end user, and must stay as a
//! private module.
-pub use serde_json::Value;
+pub type JsonMap = Map<String, Value>;
+
+pub use serde_json::{Map, Number, Value};
+pub use std::result::Result as StdResult;
pub use ::client::ClientError;
pub use ::error::{Error, Result};
diff --git a/src/lib.rs b/src/lib.rs
index 2cf569d..f34a359 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -85,7 +85,7 @@
//! [examples]: https://github.com/zeyla/serenity/tree/master/examples
//! [gateway docs]: client/gateway/index.html
#![allow(doc_markdown, inline_always, unknown_lints)]
-#![doc(html_logo_url="https://docs.austinhellyer.me/serenity/docs_header.png")]
+#![doc(html_logo_url="https://zey.moe/u/serenity$header.png")]
#![warn(enum_glob_use, if_not_else)]
#[macro_use]
@@ -94,13 +94,17 @@ extern crate bitflags;
extern crate lazy_static;
#[macro_use]
extern crate log;
+#[macro_use]
+extern crate serde_derive;
+#[macro_use]
+extern crate serde_json;
extern crate base64;
extern crate byteorder;
extern crate flate2;
extern crate hyper;
extern crate multipart;
-extern crate serde_json;
+extern crate serde;
extern crate time;
extern crate typemap;
extern crate websocket;
@@ -118,9 +122,11 @@ pub mod ext;
pub mod model;
pub mod prelude;
+#[macro_use]
+mod internal;
+
mod constants;
mod error;
-mod internal;
pub use client::Client;
pub use error::{Error, Result};
diff --git a/src/model/channel/attachment.rs b/src/model/channel/attachment.rs
index b6ce53d..17e3076 100644
--- a/src/model/channel/attachment.rs
+++ b/src/model/channel/attachment.rs
@@ -1,7 +1,28 @@
use hyper::Client as HyperClient;
use std::io::Read;
use ::internal::prelude::*;
-use ::model::Attachment;
+
+/// A file uploaded with a message. Not to be confused with [`Embed`]s.
+///
+/// [`Embed`]: struct.Embed.html
+#[derive(Clone, Debug, Deserialize)]
+pub struct Attachment {
+ /// The unique ID given to this attachment.
+ pub id: String,
+ /// The filename of the file that was uploaded. This is equivalent to what
+ /// the uploader had their file named.
+ pub filename: String,
+ /// If the attachment is an image, then the height of the image is provided.
+ pub height: Option<u64>,
+ /// The proxy URL.
+ pub proxy_url: String,
+ /// The size of the file in bytes.
+ pub size: u64,
+ /// The URL of the uploaded attachment.
+ pub url: String,
+ /// If the attachment is an image, then the widfth of the image is provided.
+ pub width: Option<u64>,
+}
impl Attachment {
/// If this attachment is an image, then a tuple of the width and height
diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs
index 7a2fb3a..f0834de 100644
--- a/src/model/channel/channel_id.rs
+++ b/src/model/channel/channel_id.rs
@@ -1,4 +1,3 @@
-use serde_json::builder::ObjectBuilder;
use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
use std::io::Read;
use ::client::rest;
@@ -54,12 +53,12 @@ impl ChannelId {
PermissionOverwriteType::Role(id) => (id.0, "role"),
};
- let map = ObjectBuilder::new()
- .insert("allow", target.allow.bits())
- .insert("deny", target.deny.bits())
- .insert("id", id)
- .insert("type", kind)
- .build();
+ let map = json!({
+ "allow": target.allow.bits(),
+ "deny": target.deny.bits(),
+ "id": id,
+ "type": kind,
+ });
rest::create_permission(self.0, id, &map)
}
@@ -122,7 +121,9 @@ impl ChannelId {
.map(|message_id| message_id.0)
.collect::<Vec<u64>>();
- let map = ObjectBuilder::new().insert("messages", ids).build();
+ let map = json!({
+ "messages": ids
+ });
rest::delete_messages(self.0, &map)
}
@@ -181,7 +182,7 @@ impl ChannelId {
/// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
#[inline]
pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F) -> Result<GuildChannel> {
- rest::edit_channel(self.0, &f(EditChannel::default()).0.build())
+ rest::edit_channel(self.0, &f(EditChannel::default()).0)
}
/// Edits a [`Message`] in the channel given its Id.
diff --git a/src/model/channel/embed.rs b/src/model/channel/embed.rs
index 32c3722..b278622 100644
--- a/src/model/channel/embed.rs
+++ b/src/model/channel/embed.rs
@@ -1,6 +1,57 @@
-use serde_json::Value;
-use ::model::Embed;
use ::utils::builder::CreateEmbed;
+use ::utils::Colour;
+use ::internal::prelude::*;
+
+/// Represents a rich embed which allows using richer markdown, multiple fields
+/// and more. This was heavily inspired by [slack's attachments].
+///
+/// You can include an attachment in your own message by a user or a bot, or in
+/// a webhook.
+///
+/// **Note**: Maximum amount of characters you can put is 256 in a field name,
+/// 1024 in a field value, and 2048 in a description.
+///
+/// [slack's attachments]: https://api.slack.com/docs/message-attachments
+#[derive(Clone, Debug, Deserialize)]
+pub struct Embed {
+ /// Information about the author of the embed.
+ pub author: Option<EmbedAuthor>,
+ /// The colour code of the embed.
+ #[serde(default, rename="color")]
+ pub colour: Colour,
+ /// The description of the embed.
+ ///
+ /// The maximum value for this field is 2048 unicode codepoints.
+ pub description: Option<String>,
+ /// The array of fields.
+ ///
+ /// The maximum number of fields is 25.
+ #[serde(default)]
+ pub fields: Vec<EmbedField>,
+ /// Image information of the embed.
+ pub image: Option<EmbedImage>,
+ /// The type of the embed. For embeds not generated by Discord's backend,
+ /// this will always be "rich".
+ #[serde(rename="type")]
+ pub kind: String,
+ /// Provider information for the embed.
+ ///
+ /// For example, if the embed [`kind`] is `"video"`, the provider might
+ /// contain information about YouTube.
+ pub provider: Option<EmbedProvider>,
+ /// Thumbnail information of the embed.
+ pub thumbnail: Option<EmbedThumbnail>,
+ /// Timestamp information.
+ pub timestamp: Option<String>,
+ /// The title of the embed.
+ pub title: Option<String>,
+ /// The URL of the embed.
+ pub url: Option<String>,
+ /// The embed's video information.
+ ///
+ /// This is present if the [`kind`] is `"video"`.
+ pub video: Option<EmbedVideo>,
+}
impl Embed {
/// Creates a fake Embed, giving back a `serde_json` map.
@@ -13,3 +64,96 @@ impl Embed {
Value::Object(f(CreateEmbed::default()).0)
}
}
+
+/// An author object in an embed.
+#[derive(Clone, Debug, Deserialize)]
+pub struct EmbedAuthor {
+ /// The URL of the author icon.
+ ///
+ /// This only supports HTTP(S).
+ pub icon_url: Option<String>,
+ /// The name of the author.
+ pub name: String,
+ /// A proxied URL of the author icon.
+ pub proxy_icon_url: Option<String>,
+ /// The URL of the author.
+ pub url: Option<String>,
+}
+
+/// A field object in an embed.
+#[derive(Clone, Debug, Deserialize)]
+pub struct EmbedField {
+ /// Indicator of whether the field should display as inline.
+ pub inline: bool,
+ /// The name of the field.
+ ///
+ /// The maximum length of this field is 512 unicode codepoints.
+ pub name: String,
+ /// The value of the field.
+ ///
+ /// The maxiumum length of this field is 1024 unicode codepoints.
+ pub value: String,
+}
+
+/// Footer information for an embed.
+#[derive(Clone, Debug, Deserialize)]
+pub struct EmbedFooter {
+ /// The URL of the footer icon.
+ ///
+ /// This only supports HTTP(S).
+ pub icon_url: String,
+ /// A proxied URL of the footer icon.
+ pub proxy_icon_url: String,
+ /// The associated text with the footer.
+ pub text: String,
+}
+
+/// An image object in an embed.
+#[derive(Clone, Debug, Deserialize)]
+pub struct EmbedImage {
+ /// The height of the image.
+ pub height: u64,
+ /// A proxied URL of the image.
+ pub proxy_url: String,
+ /// Source URL of the image.
+ ///
+ /// This only supports HTTP(S).
+ pub url: String,
+ /// The width of the image.
+ pub width: u64,
+}
+
+/// The provider of an embed.
+#[derive(Clone, Debug, Deserialize)]
+pub struct EmbedProvider {
+ /// The name of the provider.
+ pub name: String,
+ /// The URL of the provider.
+ pub url: Option<String>,
+}
+
+/// The dimensions and URL of an embed thumbnail.
+#[derive(Clone, Debug, Deserialize)]
+pub struct EmbedThumbnail {
+ /// The height of the thumbnail in pixels.
+ pub height: u64,
+ /// A proxied URL of the thumbnail.
+ pub proxy_url: String,
+ /// The source URL of the thumbnail.
+ ///
+ /// This only supports HTTP(S).
+ pub url: String,
+ /// The width of the thumbnail in pixels.
+ pub width: u64,
+}
+
+/// Video information for an embed.
+#[derive(Clone, Debug, Deserialize)]
+pub struct EmbedVideo {
+ /// The height of the video in pixels.
+ pub height: u64,
+ /// The source URL of the video.
+ pub url: String,
+ /// The width of the video in pixels.
+ pub width: u64,
+}
diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs
index f287e9a..21e9606 100644
--- a/src/model/channel/group.rs
+++ b/src/model/channel/group.rs
@@ -5,6 +5,31 @@ use ::client::rest;
use ::model::*;
use ::utils::builder::{CreateMessage, GetMessages};
+/// A group channel - potentially including other [`User`]s - separate from a
+/// [`Guild`].
+///
+/// [`Guild`]: struct.Guild.html
+/// [`User`]: struct.User.html
+#[derive(Clone, Debug, Deserialize)]
+pub struct Group {
+ /// The Id of the group channel.
+ #[serde(rename="id")]
+ pub channel_id: ChannelId,
+ /// The optional icon of the group channel.
+ pub icon: Option<String>,
+ /// The Id of the last message sent.
+ pub last_message_id: Option<MessageId>,
+ /// Timestamp of the latest pinned message.
+ pub last_pin_timestamp: Option<String>,
+ /// The name of the group channel.
+ pub name: Option<String>,
+ /// The Id of the group owner.
+ pub owner_id: UserId,
+ /// A map of the group's recipients.
+ #[serde(deserialize_with="deserialize_users")]
+ pub recipients: HashMap<UserId, Arc<RwLock<User>>>,
+}
+
impl Group {
/// Adds the given user to the group. If the user is already in the group,
/// then nothing is done.
diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs
index e4a84c7..eaefb70 100644
--- a/src/model/channel/guild_channel.rs
+++ b/src/model/channel/guild_channel.rs
@@ -1,4 +1,3 @@
-use serde_json::builder::ObjectBuilder;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::Read;
use std::mem;
@@ -10,6 +9,58 @@ use ::utils::builder::{CreateInvite, CreateMessage, EditChannel, GetMessages};
#[cfg(feature="cache")]
use ::client::CACHE;
+/// Represents a guild's text or voice channel. Some methods are available only
+/// for voice channels and some are only available for text channels.
+#[derive(Clone, Debug, Deserialize)]
+pub struct GuildChannel {
+ /// The unique Id of the channel.
+ ///
+ /// The default channel Id shares the Id of the guild and the default role.
+ pub id: ChannelId,
+ /// The bitrate of the channel.
+ ///
+ /// **Note**: This is only available for voice channels.
+ pub bitrate: Option<u64>,
+ /// The Id of the guild the channel is located in.
+ ///
+ /// If this matches with the [`id`], then this is the default text channel.
+ ///
+ /// The original voice channel has an Id equal to the guild's Id,
+ /// incremented by one.
+ pub guild_id: GuildId,
+ /// The type of the channel.
+ #[serde(rename="type")]
+ pub kind: ChannelType,
+ /// The Id of the last message sent in the channel.
+ ///
+ /// **Note**: This is only available for text channels.
+ pub last_message_id: Option<MessageId>,
+ /// The timestamp of the time a pin was most recently made.
+ ///
+ /// **Note**: This is only available for text channels.
+ pub last_pin_timestamp: Option<String>,
+ /// The name of the channel.
+ pub name: String,
+ /// Permission overwrites for [`Member`]s and for [`Role`]s.
+ ///
+ /// [`Member`]: struct.Member.html
+ /// [`Role`]: struct.Role.html
+ pub permission_overwrites: Vec<PermissionOverwrite>,
+ /// The position of the channel.
+ ///
+ /// The default text channel will _almost always_ have a position of `-1` or
+ /// `0`.
+ pub position: i64,
+ /// The topic of the channel.
+ ///
+ /// **Note**: This is only available for text channels.
+ pub topic: Option<String>,
+ /// The maximum number of members allowed in the channel.
+ ///
+ /// **Note**: This is only available for voice channels.
+ pub user_limit: Option<u64>,
+}
+
impl GuildChannel {
/// Broadcasts to the channel that the current user is typing.
///
@@ -48,9 +99,7 @@ impl GuildChannel {
}
}
- let map = f(CreateInvite::default()).0.build();
-
- rest::create_invite(self.id.0, &map)
+ rest::create_invite(self.id.0, &f(CreateInvite::default()).0)
}
/// Creates a [permission overwrite][`PermissionOverwrite`] for either a
@@ -130,34 +179,6 @@ impl GuildChannel {
self.id.create_permission(target)
}
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<GuildChannel> {
- let mut map = into_map(value)?;
-
- let id = remove(&mut map, "guild_id").and_then(GuildId::decode)?;
-
- GuildChannel::decode_guild(Value::Object(map), id)
- }
-
- #[doc(hidden)]
- pub fn decode_guild(value: Value, guild_id: GuildId) -> Result<GuildChannel> {
- let mut map = into_map(value)?;
-
- Ok(GuildChannel {
- id: remove(&mut map, "id").and_then(ChannelId::decode)?,
- name: remove(&mut map, "name").and_then(into_string)?,
- guild_id: guild_id,
- topic: opt(&mut map, "topic", into_string)?,
- position: req!(remove(&mut map, "position")?.as_i64()),
- kind: remove(&mut map, "type").and_then(ChannelType::decode)?,
- last_message_id: opt(&mut map, "last_message_id", MessageId::decode)?,
- permission_overwrites: decode_array(remove(&mut map, "permission_overwrites")?, PermissionOverwrite::decode)?,
- bitrate: remove(&mut map, "bitrate").ok().and_then(|v| v.as_u64()),
- user_limit: remove(&mut map, "user_limit").ok().and_then(|v| v.as_u64()),
- last_pin_timestamp: opt(&mut map, "last_pin_timestamp", into_string)?,
- })
- }
-
/// Deletes this channel, returning the channel on a successful deletion.
pub fn delete(&self) -> Result<Channel> {
#[cfg(feature="cache")]
@@ -238,12 +259,12 @@ impl GuildChannel {
}
}
- let map = ObjectBuilder::new()
- .insert("name", &self.name)
- .insert("position", self.position)
- .insert("type", self.kind.name());
+ let mut map = Map::new();
+ map.insert("name".to_owned(), Value::String(self.name.clone()));
+ map.insert("position".to_owned(), Value::Number(Number::from(self.position)));
+ map.insert("type".to_owned(), Value::String(self.kind.name().to_owned()));
- let edited = f(EditChannel(map)).0.build();
+ let edited = f(EditChannel(map)).0;
match rest::edit_channel(self.id.0, &edited) {
Ok(channel) => {
diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs
index 709ce9a..029914d 100644
--- a/src/model/channel/message.rs
+++ b/src/model/channel/message.rs
@@ -1,4 +1,3 @@
-use serde_json::builder::ObjectBuilder;
use std::mem;
use ::constants;
use ::client::rest;
@@ -8,6 +7,63 @@ use ::utils::builder::{CreateEmbed, CreateMessage};
#[cfg(feature="cache")]
use ::client::CACHE;
+/// A representation of a message over a guild's text channel, a group, or a
+/// private channel.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Message {
+ /// The unique Id of the message. Can be used to calculate the creation date
+ /// of the message.
+ pub id: MessageId,
+ /// An array of the files attached to a message.
+ pub attachments: Vec<Attachment>,
+ /// The user that sent the message.
+ pub author: User,
+ /// The Id of the [`Channel`] that the message was sent to.
+ ///
+ /// [`Channel`]: enum.Channel.html
+ pub channel_id: ChannelId,
+ /// The content of the message.
+ pub content: String,
+ /// The timestamp of the last time the message was updated, if it was.
+ pub edited_timestamp: Option<String>,
+ /// Array of embeds sent with the message.
+ pub embeds: Vec<Embed>,
+ /// Whether the message is the "found" message in a search.
+ ///
+ /// Note that this is only relevant in the context of searches, and will
+ /// otherwise always be `false`.
+ #[serde(default)]
+ pub hit: bool,
+ /// Indicator of the type of message this is, i.e. whether it is a regular
+ /// message or a system message.
+ #[serde(rename="type")]
+ pub kind: MessageType,
+ /// Indicator of whether the message mentions everyone.
+ pub mention_everyone: bool,
+ /// Array of [`Role`]s' Ids mentioned in the message.
+ ///
+ /// [`Role`]: struct.Role.html
+ pub mention_roles: Vec<RoleId>,
+ /// Array of users mentioned in the message.
+ pub mentions: Vec<User>,
+ /// Non-repeating number used for ensuring message order.
+ pub nonce: Option<String>,
+ /// Indicator of whether the message is pinned.
+ pub pinned: bool,
+ /// Array of reactions performed on the message.
+ #[serde(default)]
+ pub reactions: Vec<MessageReaction>,
+ /// Initial message creation timestamp, calculated from its Id.
+ pub timestamp: String,
+ /// Indicator of whether the command is to be played back via
+ /// text-to-speech.
+ ///
+ /// In the client, this is done via the `/tts` slash command.
+ pub tts: bool,
+ /// The Id of the webhook that sent this message, if one did.
+ pub webhook_id: Option<WebhookId>,
+}
+
impl Message {
/// Deletes the message.
///
@@ -328,10 +384,10 @@ impl Message {
gen.push_str(": ");
gen.push_str(content);
- let map = ObjectBuilder::new()
- .insert("content", gen)
- .insert("tts", false)
- .build();
+ let map = json!({
+ "content": gen,
+ "tts": false,
+ });
rest::send_message(self.channel_id.0, &map)
}
@@ -368,3 +424,62 @@ impl From<Message> for MessageId {
message.id
}
}
+
+/// A representation of a reaction to a message.
+///
+/// Multiple of the same [reaction type] are sent into one `MessageReaction`,
+/// with an associated [`count`].
+///
+/// [`count`]: #structfield.count
+/// [reaction type]: enum.ReactionType.html
+#[derive(Clone, Debug, Deserialize)]
+pub struct MessageReaction {
+ /// The amount of the type of reaction that have been sent for the
+ /// associated message.
+ pub count: u64,
+ /// Indicator of whether the current user has sent the type of reaction.
+ pub me: bool,
+ /// The type of reaction.
+ #[serde(rename="emoji")]
+ pub reaction_type: ReactionType,
+}
+
+enum_number!(
+ /// Differentiates between regular and different types of system messages.
+ MessageType {
+ /// A regular message.
+ Regular = 0,
+ /// An indicator that a recipient was added by the author.
+ GroupRecipientAddition = 1,
+ /// An indicator that a recipient was removed by the author.
+ GroupRecipientRemoval = 2,
+ /// An indicator that a call was started by the author.
+ GroupCallCreation = 3,
+ /// An indicator that the group name was modified by the author.
+ GroupNameUpdate = 4,
+ /// An indicator that the group icon was modified by the author.
+ GroupIconUpdate = 5,
+ /// An indicator that a message was pinned by the author.
+ PinsAdd = 6,
+ }
+);
+
+/// An emoji reaction to a message.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Reaction {
+ /// The [`Channel`] of the associated [`Message`].
+ ///
+ /// [`Channel`]: enum.Channel.html
+ /// [`Message`]: struct.Message.html
+ pub channel_id: ChannelId,
+ /// The reactive emoji used.
+ pub emoji: ReactionType,
+ /// The Id of the [`Message`] that was reacted to.
+ ///
+ /// [`Message`]: struct.Message.html
+ pub message_id: MessageId,
+ /// The Id of the [`User`] that sent the reaction.
+ ///
+ /// [`User`]: struct.User.html
+ pub user_id: UserId,
+}
diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs
index 3ea765a..4877785 100644
--- a/src/model/channel/mod.rs
+++ b/src/model/channel/mod.rs
@@ -16,11 +16,31 @@ pub use self::message::*;
pub use self::private_channel::*;
pub use self::reaction::*;
+use serde::de::Error as DeError;
+use serde_json;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::Read;
use ::model::*;
use ::utils::builder::{CreateMessage, GetMessages};
+/// A container for any channel.
+#[derive(Clone, Debug)]
+pub enum Channel {
+ /// A group. A group comprises of only one channel.
+ Group(Arc<RwLock<Group>>),
+ /// A [text] or [voice] channel within a [`Guild`].
+ ///
+ /// [`Guild`]: struct.Guild.html
+ /// [text]: enum.ChannelType.html#variant.Text
+ /// [voice]: enum.ChannelType.html#variant.Voice
+ Guild(Arc<RwLock<GuildChannel>>),
+ /// A private channel to another [`User`]. No other users may access the
+ /// channel. For multi-user "private channels", use a group.
+ ///
+ /// [`User`]: struct.User.html
+ Private(Arc<RwLock<PrivateChannel>>),
+}
+
impl Channel {
/// React to a [`Message`] with a custom [`Emoji`] or unicode character.
///
@@ -40,21 +60,6 @@ impl Channel {
self.id().create_reaction(message_id, reaction_type)
}
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Channel> {
- let map = into_map(value)?;
- match req!(map.get("type").and_then(|x| x.as_u64())) {
- 0 | 2 => GuildChannel::decode(Value::Object(map))
- .map(|x| Channel::Guild(Arc::new(RwLock::new(x)))),
- 1 => PrivateChannel::decode(Value::Object(map))
- .map(|x| Channel::Private(Arc::new(RwLock::new(x)))),
- 3 => Group::decode(Value::Object(map))
- .map(|x| Channel::Group(Arc::new(RwLock::new(x)))),
- other => Err(Error::Decode("Expected value Channel type",
- Value::U64(other))),
- }
- }
-
/// Deletes the inner channel.
///
/// **Note**: There is no real function as _deleting_ a [`Group`]. The
@@ -307,6 +312,30 @@ impl Channel {
}
}
+impl Deserialize for Channel {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ let v = JsonMap::deserialize(deserializer)?;
+ let kind = {
+ let kind = v.get("type").ok_or_else(|| DeError::missing_field("type"))?;
+
+ kind.as_u64().unwrap()
+ };
+
+ match kind {
+ 0 | 2 => serde_json::from_value::<GuildChannel>(Value::Object(v))
+ .map(|x| Channel::Guild(Arc::new(RwLock::new(x))))
+ .map_err(DeError::custom),
+ 1 => serde_json::from_value::<PrivateChannel>(Value::Object(v))
+ .map(|x| Channel::Private(Arc::new(RwLock::new(x))))
+ .map_err(DeError::custom),
+ 3 => serde_json::from_value::<Group>(Value::Object(v))
+ .map(|x| Channel::Group(Arc::new(RwLock::new(x))))
+ .map_err(DeError::custom),
+ _ => Err(DeError::custom("Unknown channel type")),
+ }
+ }
+}
+
impl Display for Channel {
/// Formats the channel into a "mentioned" string.
///
@@ -339,22 +368,101 @@ impl Display for Channel {
}
}
-impl PermissionOverwrite {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<PermissionOverwrite> {
- let mut map = into_map(value)?;
- let id = remove(&mut map, "id").and_then(decode_id)?;
- let kind = remove(&mut map, "type").and_then(into_string)?;
- let kind = match &*kind {
- "member" => PermissionOverwriteType::Member(UserId(id)),
- "role" => PermissionOverwriteType::Role(RoleId(id)),
- _ => return Err(Error::Decode("Expected valid PermissionOverwrite type", Value::String(kind))),
+enum_number!(
+ /// A representation of a type of channel.
+ ChannelType {
+ #[doc="An indicator that the channel is a text [`GuildChannel`].
+
+[`GuildChannel`]: struct.GuildChannel.html"]
+ Text = 0,
+ #[doc="An indicator that the channel is a [`PrivateChannel`].
+
+[`PrivateChannel`]: struct.PrivateChannel.html"]
+ Private = 1,
+ #[doc="An indicator that the channel is a voice [`GuildChannel`].
+
+[`GuildChannel`]: struct.GuildChannel.html"]
+ Voice = 2,
+ #[doc="An indicator that the channel is the channel of a [`Group`].
+
+[`Group`]: struct.Group.html"]
+ Group = 3,
+ }
+);
+
+impl ChannelType {
+ pub fn name(&self) -> &str {
+ match *self {
+ ChannelType::Group => "group",
+ ChannelType::Private => "private",
+ ChannelType::Text => "text",
+ ChannelType::Voice => "voice",
+ }
+ }
+}
+
+#[derive(Deserialize)]
+struct PermissionOverwriteData {
+ allow: Permissions,
+ deny: Permissions,
+ id: u64,
+ #[serde(rename="type")]
+ kind: String,
+}
+
+/// A channel-specific permission overwrite for a member or role.
+#[derive(Clone, Debug)]
+pub struct PermissionOverwrite {
+ pub allow: Permissions,
+ pub deny: Permissions,
+ pub kind: PermissionOverwriteType,
+}
+
+impl Deserialize for PermissionOverwrite {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<PermissionOverwrite, D::Error> {
+ let data = PermissionOverwriteData::deserialize(deserializer)?;
+
+ let kind = match &data.kind[..] {
+ "member" => PermissionOverwriteType::Member(UserId(data.id)),
+ "role" => PermissionOverwriteType::Role(RoleId(data.id)),
+ _ => return Err(DeError::custom("Unknown PermissionOverwriteType")),
};
Ok(PermissionOverwrite {
+ allow: data.allow,
+ deny: data.deny,
kind: kind,
- allow: remove(&mut map, "allow").and_then(Permissions::decode)?,
- deny: remove(&mut map, "deny").and_then(Permissions::decode)?,
})
}
}
+
+/// The type of edit being made to a Channel's permissions.
+///
+/// This is for use with methods such as `Context::create_permission`.
+///
+/// [`Context::create_permission`]: ../client/
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum PermissionOverwriteType {
+ /// A member which is having its permission overwrites edited.
+ Member(UserId),
+ /// A role which is having its permission overwrites edited.
+ Role(RoleId),
+}
+
+/// The results of a search, including the total results and a vector of
+/// messages.
+#[derive(Clone, Debug, Deserialize)]
+pub struct SearchResult {
+ /// An amount of messages returned from the result.
+ ///
+ /// Note that this is a vectof of a vector of messages. Each "set" of
+ /// messages contains the "found" message, as well as optional surrounding
+ /// messages for context.
+ #[serde(rename="messages")]
+ pub results: Vec<Vec<Message>>,
+ /// The number of messages directly related to the search.
+ ///
+ /// This does not count contextual messages.
+ #[serde(rename="total_results")]
+ pub total: u64,
+}
diff --git a/src/model/channel/private_channel.rs b/src/model/channel/private_channel.rs
index 8ce520c..802f84a 100644
--- a/src/model/channel/private_channel.rs
+++ b/src/model/channel/private_channel.rs
@@ -1,8 +1,34 @@
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::Read;
+use super::deserialize_single_recipient;
use ::model::*;
use ::utils::builder::{CreateMessage, GetMessages};
+/// A Direct Message text channel with another user.
+#[derive(Clone, Debug, Deserialize)]
+pub struct PrivateChannel {
+ /// The unique Id of the private channel.
+ ///
+ /// Can be used to calculate the first message's creation date.
+ pub id: ChannelId,
+ /// The Id of the last message sent.
+ pub last_message_id: Option<MessageId>,
+ /// Timestamp of the last time a [`Message`] was pinned.
+ ///
+ /// [`Message`]: struct.Message.html
+ pub last_pin_timestamp: Option<String>,
+ /// Indicator of the type of channel this is.
+ ///
+ /// This should always be [`ChannelType::Private`].
+ ///
+ /// [`ChannelType::Private`]: enum.ChannelType.html#variant.Private
+ #[serde(rename="type")]
+ pub kind: ChannelType,
+ /// The recipient to the private channel.
+ #[serde(deserialize_with="deserialize_single_recipient", rename="recipients")]
+ pub recipient: Arc<RwLock<User>>,
+}
+
impl PrivateChannel {
/// Broadcasts that the current user is typing to the recipient.
pub fn broadcast_typing(&self) -> Result<()> {
@@ -26,21 +52,6 @@ impl PrivateChannel {
self.id.create_reaction(message_id, reaction_type)
}
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<PrivateChannel> {
- let mut map = into_map(value)?;
- let mut recipients = decode_array(remove(&mut map, "recipients")?,
- User::decode)?;
-
- Ok(PrivateChannel {
- id: remove(&mut map, "id").and_then(ChannelId::decode)?,
- kind: remove(&mut map, "type").and_then(ChannelType::decode)?,
- last_message_id: opt(&mut map, "last_message_id", MessageId::decode)?,
- last_pin_timestamp: opt(&mut map, "last_pin_timestamp", into_string)?,
- recipient: Arc::new(RwLock::new(recipients.remove(0))),
- })
- }
-
/// Deletes the channel. This does not delete the contents of the channel,
/// and is equivalent to closing a private channel on the client, which can
/// be re-opened.
diff --git a/src/model/channel/reaction.rs b/src/model/channel/reaction.rs
index aa4f339..3abe7b8 100644
--- a/src/model/channel/reaction.rs
+++ b/src/model/channel/reaction.rs
@@ -96,7 +96,8 @@ impl Reaction {
/// The type of a [`Reaction`] sent.
///
/// [`Reaction`]: struct.Reaction.html
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
pub enum ReactionType {
/// A reaction with a [`Guild`]s custom [`Emoji`], which is unique to the
/// guild.
@@ -113,6 +114,7 @@ pub enum ReactionType {
name: String,
},
/// A reaction with a twemoji.
+ #[serde(rename="name")]
Unicode(String),
}
@@ -131,21 +133,6 @@ impl ReactionType {
ReactionType::Unicode(ref unicode) => unicode.clone(),
}
}
-
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Self> {
- let mut map = into_map(value)?;
- let name = remove(&mut map, "name").and_then(into_string)?;
-
- // Only custom emoji reactions (`ReactionType::Custom`) have an Id.
- Ok(match opt(&mut map, "id", EmojiId::decode)? {
- Some(id) => ReactionType::Custom {
- id: id,
- name: name,
- },
- None => ReactionType::Unicode(name),
- })
- }
}
impl From<Emoji> for ReactionType {
diff --git a/src/model/event.rs b/src/model/event.rs
index f57cb55..e0f1993 100644
--- a/src/model/event.rs
+++ b/src/model/event.rs
@@ -1,13 +1,11 @@
//! All the events this library handles.
-use std::collections::{BTreeMap, HashMap};
-use super::utils::*;
+use serde::de::Error as DeError;
+use serde_json::{self, Error as JsonError};
+use std::collections::HashMap;
use super::*;
use ::constants::{OpCode, VoiceOpCode};
use ::internal::prelude::*;
-use ::utils::decode_array;
-
-type Map = BTreeMap<String, Value>;
/// Event data for the channel creation event.
///
@@ -27,147 +25,85 @@ pub struct ChannelCreateEvent {
pub channel: Channel,
}
-impl ChannelCreateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(ChannelCreateEvent {
- channel: Channel::decode(Value::Object(map))?,
+impl Deserialize for ChannelCreateEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ channel: Channel::deserialize(deserializer)?,
})
}
}
-
#[derive(Clone, Debug)]
pub struct ChannelDeleteEvent {
pub channel: Channel,
}
-impl ChannelDeleteEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(ChannelDeleteEvent {
- channel: Channel::decode(Value::Object(map))?,
+impl Deserialize for ChannelDeleteEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ channel: Channel::deserialize(deserializer)?,
})
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct ChannelPinsAckEvent {
pub channel_id: ChannelId,
pub timestamp: String,
}
-impl ChannelPinsAckEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(ChannelPinsAckEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- timestamp: remove(&mut map, "timestamp").and_then(into_string)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct ChannelPinsUpdateEvent {
pub channel_id: ChannelId,
pub last_pin_timestamp: Option<String>,
}
-impl ChannelPinsUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(ChannelPinsUpdateEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- last_pin_timestamp: opt(&mut map, "last_pin_timestamp", into_string)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct ChannelRecipientAddEvent {
pub channel_id: ChannelId,
pub user: User,
}
-impl ChannelRecipientAddEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(ChannelRecipientAddEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- user: remove(&mut map, "user").and_then(User::decode)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct ChannelRecipientRemoveEvent {
pub channel_id: ChannelId,
pub user: User,
}
-impl ChannelRecipientRemoveEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(ChannelRecipientRemoveEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- user: remove(&mut map, "user").and_then(User::decode)?,
- })
- }
-}
-
#[derive(Clone, Debug)]
pub struct ChannelUpdateEvent {
pub channel: Channel,
}
-impl ChannelUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(ChannelUpdateEvent {
- channel: Channel::decode(Value::Object(map))?,
+impl Deserialize for ChannelUpdateEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ channel: Channel::deserialize(deserializer)?,
})
}
}
-#[derive(Clone, Debug)]
-pub struct GuildBanAddEvent {
- pub guild_id: GuildId,
- pub user: User,
+#[derive(Clone, Debug, Deserialize)]
+pub struct FriendSuggestionCreateEvent {
+ pub reasons: Vec<SuggestionReason>,
+ pub suggested_user: User,
}
-impl GuildBanAddEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildBanAddEvent {
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- user: remove(&mut map, "user").and_then(User::decode)?,
- })
- }
+#[derive(Clone, Copy, Debug, Deserialize)]
+pub struct FriendSuggestionDeleteEvent {
+ pub suggested_user_id: UserId,
}
-#[derive(Clone, Debug)]
-pub struct GuildBanRemoveEvent {
+#[derive(Clone, Debug, Deserialize)]
+pub struct GuildBanAddEvent {
pub guild_id: GuildId,
pub user: User,
}
-impl GuildBanRemoveEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildBanRemoveEvent {
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- user: remove(&mut map, "user").and_then(User::decode)?,
- })
- }
+#[derive(Clone, Debug, Deserialize)]
+pub struct GuildBanRemoveEvent {
+ pub guild_id: GuildId,
+ pub user: User,
}
#[derive(Clone, Debug)]
@@ -175,12 +111,10 @@ pub struct GuildCreateEvent {
pub guild: Guild,
}
-impl GuildCreateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(GuildCreateEvent {
- guild: Guild::decode(Value::Object(map))?,
+impl Deserialize for GuildCreateEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ guild: Guild::deserialize(deserializer)?,
})
}
}
@@ -190,85 +124,54 @@ pub struct GuildDeleteEvent {
pub guild: PartialGuild,
}
-impl GuildDeleteEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(GuildDeleteEvent {
- guild: PartialGuild::decode(Value::Object(map))?,
+impl Deserialize for GuildDeleteEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ guild: PartialGuild::deserialize(deserializer)?,
})
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct GuildEmojisUpdateEvent {
pub emojis: HashMap<EmojiId, Emoji>,
pub guild_id: GuildId,
}
-impl GuildEmojisUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildEmojisUpdateEvent {
- emojis: remove(&mut map, "emojis").and_then(decode_emojis)?,
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct GuildIntegrationsUpdateEvent {
pub guild_id: GuildId,
}
-impl GuildIntegrationsUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildIntegrationsUpdateEvent {
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- })
- }
-}
-
#[derive(Clone, Debug)]
pub struct GuildMemberAddEvent {
pub guild_id: GuildId,
pub member: Member,
}
-impl GuildMemberAddEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- let guild_id = remove(&mut map, "guild_id").and_then(GuildId::decode)?;
+impl Deserialize for GuildMemberAddEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ let map = JsonMap::deserialize(deserializer)?;
+
+ let guild_id = map.get("guild_id")
+ .ok_or_else(|| DeError::custom("missing member add guild id"))
+ .and_then(|v| GuildId::deserialize(v.clone()))
+ .map_err(DeError::custom)?;
Ok(GuildMemberAddEvent {
guild_id: guild_id,
- member: Member::decode_guild(guild_id, Value::Object(map))?,
+ member: Member::deserialize(Value::Object(map)).map_err(DeError::custom)?,
})
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct GuildMemberRemoveEvent {
pub guild_id: GuildId,
pub user: User,
}
-impl GuildMemberRemoveEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildMemberRemoveEvent {
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- user: remove(&mut map, "user").and_then(User::decode)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct GuildMemberUpdateEvent {
pub guild_id: GuildId,
pub nick: Option<String>,
@@ -276,115 +179,76 @@ pub struct GuildMemberUpdateEvent {
pub user: User,
}
-impl GuildMemberUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildMemberUpdateEvent {
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- nick: opt(&mut map, "nick", into_string)?,
- roles: decode_array(remove(&mut map, "roles")?, RoleId::decode)?,
- user: remove(&mut map, "user").and_then(User::decode)?,
- })
- }
-}
-
#[derive(Clone, Debug)]
pub struct GuildMembersChunkEvent {
pub guild_id: GuildId,
pub members: HashMap<UserId, Member>,
}
-impl GuildMembersChunkEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- let guild_id = remove(&mut map, "guild_id").and_then(GuildId::decode)?;
+impl Deserialize for GuildMembersChunkEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ let mut map = JsonMap::deserialize(deserializer)?;
+
+ let guild_id = map.get("guild_id")
+ .ok_or_else(|| DeError::custom("missing member chunk guild id"))
+ .and_then(|v| GuildId::deserialize(v.clone()))
+ .map_err(DeError::custom)?;
+
+ let mut members = map.remove("members").ok_or_else(|| DeError::custom("missing member chunk members"))?;
+
+ if let Some(members) = members.as_array_mut() {
+ let num = Value::Number(Number::from(guild_id.0));
+
+ for member in members {
+ if let Some(map) = member.as_object_mut() {
+ map.insert("guild_id".to_owned(), num.clone());
+ }
+ }
+ }
+
+ let members: HashMap<UserId, Member> = Deserialize::deserialize(members)
+ .map_err(DeError::custom)?;
Ok(GuildMembersChunkEvent {
guild_id: guild_id,
- members: remove(&mut map, "members").and_then(|x| decode_guild_members(guild_id, x))?,
+ members: members,
})
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct GuildRoleCreateEvent {
pub guild_id: GuildId,
pub role: Role,
}
-impl GuildRoleCreateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildRoleCreateEvent {
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- role: remove(&mut map, "role").and_then(Role::decode)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct GuildRoleDeleteEvent {
pub guild_id: GuildId,
pub role_id: RoleId,
}
-impl GuildRoleDeleteEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildRoleDeleteEvent {
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- role_id: remove(&mut map, "role_id").and_then(RoleId::decode)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct GuildRoleUpdateEvent {
pub guild_id: GuildId,
pub role: Role,
}
-impl GuildRoleUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildRoleUpdateEvent {
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- role: remove(&mut map, "role").and_then(Role::decode)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct GuildUnavailableEvent {
+ #[serde(rename="id")]
pub guild_id: GuildId,
}
-impl GuildUnavailableEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(GuildUnavailableEvent {
- guild_id: remove(&mut map, "id").and_then(GuildId::decode)?,
- })
- }
-}
-
#[derive(Clone, Debug)]
pub struct GuildUpdateEvent {
pub guild: PartialGuild,
}
-impl GuildUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(GuildUpdateEvent {
- guild: PartialGuild::decode(Value::Object(map))?,
+impl Deserialize for GuildUpdateEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ guild: PartialGuild::deserialize(deserializer)?,
})
}
}
@@ -394,51 +258,28 @@ pub struct MessageCreateEvent {
pub message: Message,
}
-impl MessageCreateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(MessageCreateEvent {
- message: Message::decode(Value::Object(map))?,
+impl Deserialize for MessageCreateEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ message: Message::deserialize(deserializer)?,
})
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct MessageDeleteBulkEvent {
pub channel_id: ChannelId,
pub ids: Vec<MessageId>,
}
-impl MessageDeleteBulkEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(MessageDeleteBulkEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- ids: decode_array(remove(&mut map, "ids")?, MessageId::decode)?,
- })
- }
-}
-
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Deserialize)]
pub struct MessageDeleteEvent {
pub channel_id: ChannelId,
+ #[serde(rename="id")]
pub message_id: MessageId,
}
-impl MessageDeleteEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(MessageDeleteEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- message_id: remove(&mut map, "id").and_then(MessageId::decode)?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct MessageUpdateEvent {
pub id: MessageId,
pub channel_id: ChannelId,
@@ -457,30 +298,6 @@ pub struct MessageUpdateEvent {
pub embeds: Option<Vec<Value>>,
}
-impl MessageUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(MessageUpdateEvent {
- id: remove(&mut map, "id").and_then(MessageId::decode)?,
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- kind: opt(&mut map, "type", MessageType::decode)?,
- content: opt(&mut map, "content", into_string)?,
- nonce: remove(&mut map, "nonce").and_then(into_string).ok(),
- tts: remove(&mut map, "tts").ok().and_then(|v| v.as_bool()),
- pinned: remove(&mut map, "pinned").ok().and_then(|v| v.as_bool()),
- timestamp: opt(&mut map, "timestamp", into_string)?,
- edited_timestamp: opt(&mut map, "edited_timestamp", into_string)?,
- author: opt(&mut map, "author", User::decode)?,
- mention_everyone: remove(&mut map, "mention_everyone").ok().and_then(|v| v.as_bool()),
- mentions: opt(&mut map, "mentions", |v| decode_array(v, User::decode))?,
- mention_roles: opt(&mut map, "mention_roles", |v| decode_array(v, RoleId::decode))?,
- attachments: opt(&mut map, "attachments", |v| decode_array(v, Attachment::decode))?,
- embeds: opt(&mut map, "embeds", |v| decode_array(v, Ok))?,
- })
- }
-}
-
#[derive(Clone, Debug)]
pub struct PresenceUpdateEvent {
pub guild_id: Option<GuildId>,
@@ -488,14 +305,21 @@ pub struct PresenceUpdateEvent {
pub roles: Option<Vec<RoleId>>,
}
-impl PresenceUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- let guild_id = opt(&mut map, "guild_id", GuildId::decode)?;
- let roles = opt(&mut map, "roles", |v| decode_array(v, RoleId::decode))?;
- let presence = Presence::decode(Value::Object(map))?;
- Ok(PresenceUpdateEvent {
+impl Deserialize for PresenceUpdateEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ let mut map = JsonMap::deserialize(deserializer)?;
+
+ let guild_id = match map.remove("guild_id") {
+ Some(v) => serde_json::from_value::<Option<GuildId>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let roles = match map.remove("roles") {
+ Some(v) => serde_json::from_value::<Option<Vec<RoleId>>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let presence = Presence::deserialize(Value::Object(map)).map_err(DeError::custom)?;
+
+ Ok(Self {
guild_id: guild_id,
presence: presence,
roles: roles,
@@ -508,12 +332,12 @@ pub struct PresencesReplaceEvent {
pub presences: Vec<Presence>,
}
-impl PresencesReplaceEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(value: Value) -> Result<Self> {
- Ok(PresencesReplaceEvent {
- presences: decode_array(value, Presence::decode)?,
+impl Deserialize for PresencesReplaceEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ let presences: Vec<Presence> = Deserialize::deserialize(deserializer)?;
+
+ Ok(Self {
+ presences: presences,
})
}
}
@@ -523,12 +347,10 @@ pub struct ReactionAddEvent {
pub reaction: Reaction,
}
-impl ReactionAddEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(ReactionAddEvent {
- reaction: Reaction::decode(Value::Object(map))?
+impl Deserialize for ReactionAddEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ reaction: Reaction::deserialize(deserializer)?,
})
}
}
@@ -538,87 +360,51 @@ pub struct ReactionRemoveEvent {
pub reaction: Reaction,
}
-impl ReactionRemoveEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(ReactionRemoveEvent {
- reaction: Reaction::decode(Value::Object(map))?
+impl Deserialize for ReactionRemoveEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ reaction: Reaction::deserialize(deserializer)?,
})
}
}
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Deserialize)]
pub struct ReactionRemoveAllEvent {
pub channel_id: ChannelId,
pub message_id: MessageId,
}
-impl ReactionRemoveAllEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(ReactionRemoveAllEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- message_id: remove(&mut map, "message_id").and_then(MessageId::decode)?,
- })
- }
-}
-
/// The "Ready" event, containing initial ready cache
#[derive(Clone, Debug)]
pub struct ReadyEvent {
pub ready: Ready,
}
-impl ReadyEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(ReadyEvent {
- ready: Ready::decode(Value::Object(map))?,
+impl Deserialize for ReadyEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ ready: Ready::deserialize(deserializer)?,
})
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct ResumedEvent {
+ #[serde(rename="_trace")]
pub trace: Vec<Option<String>>,
}
-impl ResumedEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(ResumedEvent {
- trace: remove(&mut map, "_trace").and_then(|v| decode_array(v, |v| Ok(into_string(v).ok())))?,
- })
- }
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct TypingStartEvent {
pub channel_id: ChannelId,
pub timestamp: u64,
pub user_id: UserId,
}
-impl TypingStartEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(TypingStartEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- timestamp: req!(remove(&mut map, "timestamp")?.as_u64()),
- user_id: remove(&mut map, "user_id").and_then(UserId::decode)?,
- })
- }
-}
-
#[derive(Clone, Debug)]
pub struct UnknownEvent {
pub kind: String,
- pub value: BTreeMap<String, Value>
+ pub value: Value,
}
#[derive(Clone, Debug)]
@@ -626,17 +412,15 @@ pub struct UserUpdateEvent {
pub current_user: CurrentUser,
}
-impl UserUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(map: Map) -> Result<Self> {
- Ok(UserUpdateEvent {
- current_user: CurrentUser::decode(Value::Object(map))?,
+impl Deserialize for UserUpdateEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Self {
+ current_user: CurrentUser::deserialize(deserializer)?,
})
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct VoiceServerUpdateEvent {
pub channel_id: Option<ChannelId>,
pub endpoint: Option<String>,
@@ -644,53 +428,33 @@ pub struct VoiceServerUpdateEvent {
pub token: String,
}
-impl VoiceServerUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(VoiceServerUpdateEvent {
- guild_id: opt(&mut map, "guild_id", GuildId::decode)?,
- channel_id: opt(&mut map, "channel_id", ChannelId::decode)?,
- endpoint: opt(&mut map, "endpoint", into_string)?,
- token: remove(&mut map, "token").and_then(into_string)?,
- })
- }
-}
-
#[derive(Clone, Debug)]
pub struct VoiceStateUpdateEvent {
pub guild_id: Option<GuildId>,
pub voice_state: VoiceState,
}
-impl VoiceStateUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
+impl Deserialize for VoiceStateUpdateEvent {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ let map = JsonMap::deserialize(deserializer)?;
+ let guild_id = match map.get("guild_id") {
+ Some(v) => Some(GuildId::deserialize(v.clone()).map_err(DeError::custom)?),
+ None => None,
+ };
+
Ok(VoiceStateUpdateEvent {
- guild_id: opt(&mut map, "guild_id", GuildId::decode)?,
- voice_state: VoiceState::decode(Value::Object(map))?,
+ guild_id: guild_id,
+ voice_state: VoiceState::deserialize(Value::Object(map)).map_err(DeError::custom)?,
})
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct WebhookUpdateEvent {
pub channel_id: ChannelId,
pub guild_id: GuildId,
}
-impl WebhookUpdateEvent {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(WebhookUpdateEvent {
- channel_id: remove(&mut map, "channel_id").and_then(ChannelId::decode)?,
- guild_id: remove(&mut map, "guild_id").and_then(GuildId::decode)?,
- })
- }
-}
-
#[allow(large_enum_variant)]
#[derive(Debug, Clone)]
pub enum GatewayEvent {
@@ -704,32 +468,47 @@ pub enum GatewayEvent {
impl GatewayEvent {
pub fn decode(value: Value) -> Result<Self> {
- let mut value = into_map(value)?;
-
- let op = req!(value.get("op").and_then(|x| x.as_u64()));
-
- match OpCode::from_num(op).ok_or(Error::Client(ClientError::InvalidOpCode))? {
- OpCode::Event => Ok(GatewayEvent::Dispatch(
- req!(remove(&mut value, "s")?.as_u64()),
- Event::decode(
- remove(&mut value, "t").and_then(into_string)?,
- remove(&mut value, "d")?
- )?
- )),
+ let mut map = JsonMap::deserialize(value)?;
+
+ let op = map.remove("op")
+ .ok_or_else(|| DeError::custom("expected gateway event op"))
+ .and_then(OpCode::deserialize)?;
+
+ Ok(match op {
+ OpCode::Event => {
+ let s = map.remove("s")
+ .ok_or_else(|| DeError::custom("expected gateway event sequence"))
+ .and_then(u64::deserialize)?;
+ let t = map.remove("t")
+ .ok_or_else(|| DeError::custom("expected gateway event type"))
+ .and_then(String::deserialize)?;
+ let d = map.remove("d")
+ .ok_or_else(|| Error::Decode("expected gateway event d", Value::Object(map)))?;
+
+ GatewayEvent::Dispatch(s, Event::decode(t, d)?)
+ },
OpCode::Heartbeat => {
- Ok(GatewayEvent::Heartbeat(req!(remove(&mut value, "s")?
- .as_u64())))
+ let s = map.remove("s")
+ .ok_or_else(|| DeError::custom("Expected heartbeat s"))
+ .and_then(u64::deserialize)?;
+
+ GatewayEvent::Heartbeat(s)
},
- OpCode::Reconnect => Ok(GatewayEvent::Reconnect),
- OpCode::InvalidSession => Ok(GatewayEvent::InvalidateSession),
+ OpCode::Reconnect => GatewayEvent::Reconnect,
+ OpCode::InvalidSession => GatewayEvent::InvalidateSession,
OpCode::Hello => {
- let mut data = remove(&mut value, "d").and_then(into_map)?;
- let interval = req!(remove(&mut data, "heartbeat_interval")?.as_u64());
- Ok(GatewayEvent::Hello(interval))
+ let mut d = map.remove("d")
+ .ok_or_else(|| DeError::custom("expected gateway hello d"))
+ .and_then(JsonMap::deserialize)?;
+ let interval = d.remove("heartbeat_interval")
+ .ok_or_else(|| DeError::custom("expected gateway hello interval"))
+ .and_then(u64::deserialize)?;
+
+ GatewayEvent::Hello(interval)
},
- OpCode::HeartbeatAck => Ok(GatewayEvent::HeartbeatAck),
- _ => Err(Error::Decode("Unexpected opcode", Value::Object(value))),
- }
+ OpCode::HeartbeatAck => GatewayEvent::HeartbeatAck,
+ _ => return Err(Error::Client(ClientError::InvalidOpCode)),
+ })
}
}
@@ -857,61 +636,60 @@ pub enum Event {
impl Event {
#[allow(cyclomatic_complexity)]
fn decode(kind: String, value: Value) -> Result<Event> {
- if kind == "PRESENCES_REPLACE" {
- return Ok(Event::PresencesReplace(PresencesReplaceEvent::decode(value)?));
- }
-
- let mut value = into_map(value)?;
-
Ok(match &kind[..] {
- "CHANNEL_CREATE" => Event::ChannelCreate(ChannelCreateEvent::decode(value)?),
- "CHANNEL_DELETE" => Event::ChannelDelete(ChannelDeleteEvent::decode(value)?),
- "CHANNEL_PINS_ACK" => Event::ChannelPinsAck(ChannelPinsAckEvent::decode(value)?),
- "CHANNEL_PINS_UPDATE" => Event::ChannelPinsUpdate(ChannelPinsUpdateEvent::decode(value)?),
- "CHANNEL_RECIPIENT_ADD" => Event::ChannelRecipientAdd(ChannelRecipientAddEvent::decode(value)?),
- "CHANNEL_RECIPIENT_REMOVE" => Event::ChannelRecipientRemove(ChannelRecipientRemoveEvent::decode(value)?),
- "CHANNEL_UPDATE" => Event::ChannelUpdate(ChannelUpdateEvent::decode(value)?),
- "GUILD_BAN_ADD" => Event::GuildBanAdd(GuildBanAddEvent::decode(value)?),
- "GUILD_BAN_REMOVE" => Event::GuildBanRemove(GuildBanRemoveEvent::decode(value)?),
+ "CHANNEL_CREATE" => Event::ChannelCreate(ChannelCreateEvent::deserialize(value)?),
+ "CHANNEL_DELETE" => Event::ChannelDelete(ChannelDeleteEvent::deserialize(value)?),
+ "CHANNEL_PINS_ACK" => Event::ChannelPinsAck(ChannelPinsAckEvent::deserialize(value)?),
+ "CHANNEL_PINS_UPDATE" => Event::ChannelPinsUpdate(ChannelPinsUpdateEvent::deserialize(value)?),
+ "CHANNEL_RECIPIENT_ADD" => Event::ChannelRecipientAdd(ChannelRecipientAddEvent::deserialize(value)?),
+ "CHANNEL_RECIPIENT_REMOVE" => Event::ChannelRecipientRemove(ChannelRecipientRemoveEvent::deserialize(value)?),
+ "CHANNEL_UPDATE" => Event::ChannelUpdate(ChannelUpdateEvent::deserialize(value)?),
+ "GUILD_BAN_ADD" => Event::GuildBanAdd(GuildBanAddEvent::deserialize(value)?),
+ "GUILD_BAN_REMOVE" => Event::GuildBanRemove(GuildBanRemoveEvent::deserialize(value)?),
"GUILD_CREATE" => {
- if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) {
- Event::GuildUnavailable(GuildUnavailableEvent::decode(value)?)
+ let mut map = JsonMap::deserialize(value)?;
+
+ if map.remove("unavailable").and_then(|v| v.as_bool()).unwrap_or(false) {
+ Event::GuildUnavailable(GuildUnavailableEvent::deserialize(Value::Object(map))?)
} else {
- Event::GuildCreate(GuildCreateEvent::decode(value)?)
+ Event::GuildCreate(GuildCreateEvent::deserialize(Value::Object(map))?)
}
},
"GUILD_DELETE" => {
- if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) {
- Event::GuildUnavailable(GuildUnavailableEvent::decode(value)?)
+ let mut map = JsonMap::deserialize(value)?;
+
+ if map.remove("unavailable").and_then(|v| v.as_bool()).unwrap_or(false) {
+ Event::GuildUnavailable(GuildUnavailableEvent::deserialize(Value::Object(map))?)
} else {
- Event::GuildDelete(GuildDeleteEvent::decode(value)?)
+ Event::GuildDelete(GuildDeleteEvent::deserialize(Value::Object(map))?)
}
},
- "GUILD_EMOJIS_UPDATE" => Event::GuildEmojisUpdate(GuildEmojisUpdateEvent::decode(value)?),
- "GUILD_INTEGRATIONS_UPDATE" => Event::GuildIntegrationsUpdate(GuildIntegrationsUpdateEvent::decode(value)?),
- "GUILD_MEMBER_ADD" => Event::GuildMemberAdd(GuildMemberAddEvent::decode(value)?),
- "GUILD_MEMBER_REMOVE" => Event::GuildMemberRemove(GuildMemberRemoveEvent::decode(value)?),
- "GUILD_MEMBER_UPDATE" => Event::GuildMemberUpdate(GuildMemberUpdateEvent::decode(value)?),
- "GUILD_MEMBERS_CHUNK" => Event::GuildMembersChunk(GuildMembersChunkEvent::decode(value)?),
- "GUILD_ROLE_CREATE" => Event::GuildRoleCreate(GuildRoleCreateEvent::decode(value)?),
- "GUILD_ROLE_DELETE" => Event::GuildRoleDelete(GuildRoleDeleteEvent::decode(value)?),
- "GUILD_ROLE_UPDATE" => Event::GuildRoleUpdate(GuildRoleUpdateEvent::decode(value)?),
- "GUILD_UPDATE" => Event::GuildUpdate(GuildUpdateEvent::decode(value)?),
- "MESSAGE_CREATE" => Event::MessageCreate(MessageCreateEvent::decode(value)?),
- "MESSAGE_DELETE" => Event::MessageDelete(MessageDeleteEvent::decode(value)?),
- "MESSAGE_DELETE_BULK" => Event::MessageDeleteBulk(MessageDeleteBulkEvent::decode(value)?),
- "MESSAGE_REACTION_ADD" => Event::ReactionAdd(ReactionAddEvent::decode(value)?),
- "MESSAGE_REACTION_REMOVE" => Event::ReactionRemove(ReactionRemoveEvent::decode(value)?),
- "MESSAGE_REACTION_REMOVE_ALL" => Event::ReactionRemoveAll(ReactionRemoveAllEvent::decode(value)?),
- "MESSAGE_UPDATE" => Event::MessageUpdate(MessageUpdateEvent::decode(value)?),
- "PRESENCE_UPDATE" => Event::PresenceUpdate(PresenceUpdateEvent::decode(value)?),
- "READY" => Event::Ready(ReadyEvent::decode(value)?),
- "RESUMED" => Event::Resumed(ResumedEvent::decode(value)?),
- "TYPING_START" => Event::TypingStart(TypingStartEvent::decode(value)?),
- "USER_UPDATE" => Event::UserUpdate(UserUpdateEvent::decode(value)?),
- "VOICE_SERVER_UPDATE" => Event::VoiceServerUpdate(VoiceServerUpdateEvent::decode(value)?),
- "VOICE_STATE_UPDATE" => Event::VoiceStateUpdate(VoiceStateUpdateEvent::decode(value)?),
- "WEBHOOKS_UPDATE" => Event::WebhookUpdate(WebhookUpdateEvent::decode(value)?),
+ "GUILD_EMOJIS_UPDATE" => Event::GuildEmojisUpdate(GuildEmojisUpdateEvent::deserialize(value)?),
+ "GUILD_INTEGRATIONS_UPDATE" => Event::GuildIntegrationsUpdate(GuildIntegrationsUpdateEvent::deserialize(value)?),
+ "GUILD_MEMBER_ADD" => Event::GuildMemberAdd(GuildMemberAddEvent::deserialize(value)?),
+ "GUILD_MEMBER_REMOVE" => Event::GuildMemberRemove(GuildMemberRemoveEvent::deserialize(value)?),
+ "GUILD_MEMBER_UPDATE" => Event::GuildMemberUpdate(GuildMemberUpdateEvent::deserialize(value)?),
+ "GUILD_MEMBERS_CHUNK" => Event::GuildMembersChunk(GuildMembersChunkEvent::deserialize(value)?),
+ "GUILD_ROLE_CREATE" => Event::GuildRoleCreate(GuildRoleCreateEvent::deserialize(value)?),
+ "GUILD_ROLE_DELETE" => Event::GuildRoleDelete(GuildRoleDeleteEvent::deserialize(value)?),
+ "GUILD_ROLE_UPDATE" => Event::GuildRoleUpdate(GuildRoleUpdateEvent::deserialize(value)?),
+ "GUILD_UPDATE" => Event::GuildUpdate(GuildUpdateEvent::deserialize(value)?),
+ "MESSAGE_CREATE" => Event::MessageCreate(MessageCreateEvent::deserialize(value)?),
+ "MESSAGE_DELETE" => Event::MessageDelete(MessageDeleteEvent::deserialize(value)?),
+ "MESSAGE_DELETE_BULK" => Event::MessageDeleteBulk(MessageDeleteBulkEvent::deserialize(value)?),
+ "MESSAGE_REACTION_ADD" => Event::ReactionAdd(ReactionAddEvent::deserialize(value)?),
+ "MESSAGE_REACTION_REMOVE" => Event::ReactionRemove(ReactionRemoveEvent::deserialize(value)?),
+ "MESSAGE_REACTION_REMOVE_ALL" => Event::ReactionRemoveAll(ReactionRemoveAllEvent::deserialize(value)?),
+ "MESSAGE_UPDATE" => Event::MessageUpdate(MessageUpdateEvent::deserialize(value)?),
+ "PRESENCE_UPDATE" => Event::PresenceUpdate(PresenceUpdateEvent::deserialize(value)?),
+ "PRESENCES_REPLACE" => Event::PresencesReplace(PresencesReplaceEvent::deserialize(value)?),
+ "READY" => Event::Ready(ReadyEvent::deserialize(value)?),
+ "RESUMED" => Event::Resumed(ResumedEvent::deserialize(value)?),
+ "TYPING_START" => Event::TypingStart(TypingStartEvent::deserialize(value)?),
+ "USER_UPDATE" => Event::UserUpdate(UserUpdateEvent::deserialize(value)?),
+ "VOICE_SERVER_UPDATE" => Event::VoiceServerUpdate(VoiceServerUpdateEvent::deserialize(value)?),
+ "VOICE_STATE_UPDATE" => Event::VoiceStateUpdate(VoiceStateUpdateEvent::deserialize(value)?),
+ "WEBHOOKS_UPDATE" => Event::WebhookUpdate(WebhookUpdateEvent::deserialize(value)?),
_ => Event::Unknown(UnknownEvent {
kind: kind,
value: value,
@@ -921,23 +699,13 @@ impl Event {
}
#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Deserialize)]
pub struct VoiceHeartbeat {
pub heartbeat_interval: u64,
}
-impl VoiceHeartbeat {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(VoiceHeartbeat {
- heartbeat_interval: req!(remove(&mut map, "heartbeat_interval")?.as_u64()),
- })
- }
-}
-
#[allow(missing_docs)]
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct VoiceHello {
pub heartbeat_interval: u64,
pub ip: String,
@@ -946,61 +714,21 @@ pub struct VoiceHello {
pub ssrc: u32,
}
-impl VoiceHello {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(VoiceHello {
- heartbeat_interval: req!(remove(&mut map, "heartbeat_interval")?.as_u64()),
- ip: remove(&mut map, "ip").and_then(into_string)?,
- modes: decode_array(remove(&mut map, "modes")?, into_string)?,
- port: req!(remove(&mut map, "port")?.as_u64()) as u16,
- ssrc: req!(remove(&mut map, "ssrc")?.as_u64()) as u32,
- })
- }
-}
-
#[allow(missing_docs)]
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Deserialize)]
pub struct VoiceSessionDescription {
pub mode: String,
pub secret_key: Vec<u8>,
}
-impl VoiceSessionDescription {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(VoiceSessionDescription {
- mode: remove(&mut map, "mode")
- .and_then(into_string)?,
- secret_key: decode_array(remove(&mut map, "secret_key")?,
- |v| Ok(req!(v.as_u64()) as u8)
- )?,
- })
- }
-}
-
#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Deserialize)]
pub struct VoiceSpeaking {
pub speaking: bool,
pub ssrc: u32,
pub user_id: UserId,
}
-impl VoiceSpeaking {
- #[doc(hidden)]
- #[inline]
- pub fn decode(mut map: Map) -> Result<Self> {
- Ok(VoiceSpeaking {
- speaking: req!(remove(&mut map, "speaking")?.as_bool()),
- ssrc: req!(remove(&mut map, "ssrc")?.as_u64()) as u32,
- user_id: remove(&mut map, "user_id").and_then(UserId::decode)?,
- })
- }
-}
-
/// A representation of data received for [`voice`] events.
///
/// [`voice`]: ../../ext/voice/index.html
@@ -1026,20 +754,26 @@ pub enum VoiceEvent {
impl VoiceEvent {
#[doc(hidden)]
pub fn decode(value: Value) -> Result<VoiceEvent> {
- let mut value = into_map(value)?;
- let op = req!(remove(&mut value, "op")?.as_u64());
- let map = remove(&mut value, "d").and_then(into_map)?;
-
- let opcode = VoiceOpCode::from_num(op)
- .ok_or(Error::Client(ClientError::InvalidOpCode))?;
-
- Ok(match opcode {
- VoiceOpCode::Heartbeat => VoiceEvent::Heartbeat(VoiceHeartbeat::decode(map)?),
- VoiceOpCode::Hello => VoiceEvent::Hello(VoiceHello::decode(map)?),
+ let mut map = JsonMap::deserialize(value)?;
+
+ let op = match map.remove("op") {
+ Some(v) => VoiceOpCode::deserialize(v).map_err(JsonError::from).map_err(Error::from)?,
+ None => return Err(Error::Decode("expected voice event op", Value::Object(map))),
+ };
+
+ let d = match map.remove("d") {
+ Some(v) => JsonMap::deserialize(v).map_err(JsonError::from).map_err(Error::from)?,
+ None => return Err(Error::Decode("expected voice gateway d", Value::Object(map))),
+ };
+ let v = Value::Object(d);
+
+ Ok(match op {
+ VoiceOpCode::Heartbeat => VoiceEvent::Heartbeat(VoiceHeartbeat::deserialize(v)?),
+ VoiceOpCode::Hello => VoiceEvent::Hello(VoiceHello::deserialize(v)?),
VoiceOpCode::KeepAlive => VoiceEvent::KeepAlive,
- VoiceOpCode::SessionDescription => VoiceEvent::Ready(VoiceSessionDescription::decode(map)?),
- VoiceOpCode::Speaking => VoiceEvent::Speaking(VoiceSpeaking::decode(map)?),
- other => VoiceEvent::Unknown(other, Value::Object(map)),
+ VoiceOpCode::SessionDescription => VoiceEvent::Ready(VoiceSessionDescription::deserialize(v)?),
+ VoiceOpCode::Speaking => VoiceEvent::Speaking(VoiceSpeaking::deserialize(v)?),
+ other => VoiceEvent::Unknown(other, v),
})
}
}
diff --git a/src/model/gateway.rs b/src/model/gateway.rs
index dc25d0d..826f723 100644
--- a/src/model/gateway.rs
+++ b/src/model/gateway.rs
@@ -1,29 +1,40 @@
+use serde::de::Error as DeError;
+use serde_json;
use std::sync::{Arc, RwLock};
use super::utils::*;
use super::*;
-use ::internal::prelude::*;
-impl Game {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Option<Game>> {
- let mut map = into_map(value)?;
-
- let name = match map.remove("name") {
- Some(Value::Null) | None => return Ok(None),
- Some(v) => into_string(v)?,
- };
-
- if name.trim().is_empty() {
- return Ok(None);
- }
+/// A representation of the data retrieved from the bot gateway endpoint.
+///
+/// This is different from the [`Gateway`], as this includes the number of
+/// shards that Discord recommends to use for a bot user.
+///
+/// This is only applicable to bot users.
+#[derive(Clone, Debug, Deserialize)]
+pub struct BotGateway {
+ /// The number of shards that is recommended to be used by the current bot
+ /// user.
+ pub shards: u64,
+ /// The gateway to connect to.
+ pub url: String,
+}
- Ok(Some(Game {
- name: name,
- kind: opt(&mut map, "type", GameType::decode)?.unwrap_or(GameType::Playing),
- url: opt(&mut map, "url", into_string)?,
- }))
- }
+/// Representation of a game that a [`User`] is playing -- or streaming in the
+/// case that a stream URL is provided.
+#[derive(Clone, Debug)]
+pub struct Game {
+ /// The type of game status.
+ pub kind: GameType,
+ /// The name of the game being played.
+ pub name: String,
+ /// The Stream URL if [`kind`] is [`GameType::Streaming`].
+ ///
+ /// [`GameType::Streaming`]: enum.GameType.html#variant.Streaming
+ /// [`kind`]: #structfield.kind
+ pub url: Option<String>,
+}
+impl Game {
/// Creates a `Game` struct that appears as a `Playing <name>` status.
///
/// **Note**: Maximum `name` length is 128.
@@ -47,31 +58,136 @@ impl Game {
}
}
-impl Presence {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Presence> {
- let mut value = into_map(value)?;
- let mut user_map = remove(&mut value, "user").and_then(into_map)?;
+impl Deserialize for Game {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ let mut map = JsonMap::deserialize(deserializer)?;
+ let kind = map.remove("type")
+ .and_then(|v| GameType::deserialize(v).ok())
+ .unwrap_or(GameType::Playing);
+ let name = map.remove("name")
+ .and_then(|v| String::deserialize(v).ok())
+ .unwrap_or_else(String::new);
+ let url = map.remove("url").and_then(|v| serde_json::from_value::<String>(v).ok());
+
+ Ok(Game {
+ kind: kind,
+ name: name,
+ url: url
+ })
+ }
+}
+
+enum_number!(
+ /// The type of activity that is being performed when playing a game.
+ GameType {
+ /// An indicator that the user is playing a game.
+ Playing = 0,
+ /// An indicator that the user is streaming to a service.
+ Streaming = 1,
+ }
+);
+
+impl Default for GameType {
+ fn default() -> Self {
+ GameType::Playing
+ }
+}
+
+/// A representation of the data retrieved from the gateway endpoint.
+///
+/// For the bot-specific gateway, refer to [`BotGateway`].
+///
+/// [`BotGateway`]: struct.BotGateway.html
+#[derive(Clone, Debug, Deserialize)]
+pub struct Gateway {
+ /// The gateway to connect to.
+ pub url: String,
+}
+
+/// Information detailing the current online status of a [`User`].
+///
+/// [`User`]: struct.User.html
+#[derive(Clone, Debug)]
+pub struct Presence {
+ /// The game that a [`User`] is current playing.
+ ///
+ /// [`User`]: struct.User.html
+ pub game: Option<Game>,
+ /// The date of the last presence update.
+ pub last_modified: Option<u64>,
+ /// The nickname of the member, if applicable.
+ pub nick: Option<String>,
+ /// The user's online status.
+ pub status: OnlineStatus,
+ /// The Id of the [`User`]. Can be used to calculate the user's creation
+ /// date.
+ pub user_id: UserId,
+ /// The associated user instance.
+ pub user: Option<Arc<RwLock<User>>>,
+}
+
+impl Deserialize for Presence {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Presence, D::Error> {
+ let mut map = JsonMap::deserialize(deserializer)?;
+ let mut user_map = map.remove("user")
+ .ok_or_else(|| DeError::custom("expected presence user"))
+ .and_then(JsonMap::deserialize)
+ .map_err(DeError::custom)?;
let (user_id, user) = if user_map.len() > 1 {
- let user = User::decode(Value::Object(user_map))?;
- (user.id, Some(user))
+ let user = User::deserialize(Value::Object(user_map)).map_err(DeError::custom)?;
+
+ (user.id, Some(Arc::new(RwLock::new(user))))
} else {
- (remove(&mut user_map, "id").and_then(UserId::decode)?, None)
+ let user_id = user_map.remove("id")
+ .ok_or_else(|| DeError::custom("Missing presence user id"))
+ .and_then(|x| UserId::deserialize(x.clone()))
+ .map_err(DeError::custom)?;
+
+ (user_id, None)
};
- let game = match value.remove("game") {
- None | Some(Value::Null) => None,
- Some(v) => Game::decode(v)?,
+ let game = match map.remove("game") {
+ Some(v) => serde_json::from_value::<Option<Game>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let last_modified = match map.remove("last_modified") {
+ Some(v) => Some(u64::deserialize(v).map_err(DeError::custom)?),
+ None => None,
+ };
+ let nick = match map.remove("nick") {
+ Some(v) => serde_json::from_value::<Option<String>>(v).map_err(DeError::custom)?,
+ None => None,
};
+ let status = map.remove("status")
+ .ok_or_else(|| DeError::custom("expected presence status"))
+ .and_then(OnlineStatus::deserialize)
+ .map_err(DeError::custom)?;
Ok(Presence {
- user_id: user_id,
- status: remove(&mut value, "status").and_then(OnlineStatus::decode_str)?,
- last_modified: opt(&mut value, "last_modified", |v| Ok(req!(v.as_u64())))?,
game: game,
- user: user.map(RwLock::new).map(Arc::new),
- nick: opt(&mut value, "nick", into_string)?,
+ last_modified: last_modified,
+ nick: nick,
+ status: status,
+ user: user,
+ user_id: user_id,
})
}
}
+
+/// An initial set of information given after IDENTIFYing to the gateway.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Ready {
+ pub guilds: Vec<GuildStatus>,
+ #[serde(deserialize_with="deserialize_presences")]
+ pub presences: HashMap<UserId, Presence>,
+ #[serde(deserialize_with="deserialize_private_channels")]
+ pub private_channels: HashMap<ChannelId, Channel>,
+ pub session_id: String,
+ pub shard: Option<[u64; 2]>,
+ #[serde(default, rename="_trace")]
+ pub trace: Vec<String>,
+ pub user: CurrentUser,
+ #[serde(rename="v")]
+ pub version: u64,
+}
diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji.rs
index 0bb0f40..54a70d3 100644
--- a/src/model/guild/emoji.rs
+++ b/src/model/guild/emoji.rs
@@ -1,9 +1,7 @@
use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
-use ::model::{Emoji, EmojiId};
+use ::model::{EmojiId, RoleId};
#[cfg(feature="cache")]
-use serde_json::builder::ObjectBuilder;
-#[cfg(feature="cache")]
use std::mem;
#[cfg(feature="cache")]
use ::client::{CACHE, rest};
@@ -12,6 +10,30 @@ use ::internal::prelude::*;
#[cfg(feature="cache")]
use ::model::GuildId;
+/// Represents a custom guild emoji, which can either be created using the API,
+/// or via an integration. Emojis created using the API only work within the
+/// guild it was created in.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Emoji {
+ /// The Id of the emoji.
+ pub id: EmojiId,
+ /// The name of the emoji. It must be at least 2 characters long and can
+ /// only contain alphanumeric characters and underscores.
+ pub name: String,
+ /// Whether the emoji is managed via an [`Integration`] service.
+ ///
+ /// [`Integration`]: struct.Integration.html
+ pub managed: bool,
+ /// Whether the emoji name needs to be surrounded by colons in order to be
+ /// used by the client.
+ pub require_colons: bool,
+ /// A list of [`Role`]s that are allowed to use the emoji. If there are no
+ /// roles specified, then usage is unrestricted.
+ ///
+ /// [`Role`]: struct.Role.html
+ pub roles: Vec<RoleId>,
+}
+
impl Emoji {
/// Deletes the emoji.
///
@@ -39,9 +61,9 @@ impl Emoji {
pub fn edit(&mut self, name: &str) -> Result<()> {
match self.find_guild_id() {
Some(guild_id) => {
- let map = ObjectBuilder::new()
- .insert("name", name)
- .build();
+ let map = json!({
+ "name": name,
+ });
match rest::edit_emoji(guild_id.0, self.id.0, &map) {
Ok(emoji) => {
diff --git a/src/model/guild/feature.rs b/src/model/guild/feature.rs
new file mode 100644
index 0000000..cfbcabc
--- /dev/null
+++ b/src/model/guild/feature.rs
@@ -0,0 +1,25 @@
+/// A special feature, such as for VIP guilds, that a [`Guild`] has had granted
+/// to them.
+///
+/// [`Guild`]: struct.Guild.html
+#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq)]
+pub enum Feature {
+ /// The [`Guild`] can set a custom [`splash`][`Guild::splash`] image on
+ /// invite URLs.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ /// [`Guild::splash`]: struct.Guild.html#structfield.splash
+ #[serde(rename="INVITE_SPLASH")]
+ InviteSplash,
+ /// The [`Guild`] can set a Vanity URL, which is a custom-named permanent
+ /// invite code.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ #[serde(rename="VANITY_URL")]
+ VanityUrl,
+ /// The [`Guild`] has access to VIP voice channel regions.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ #[serde(rename="VIP_REGIONS")]
+ VipRegions,
+}
diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs
index d568360..1ef9a32 100644
--- a/src/model/guild/guild_id.rs
+++ b/src/model/guild/guild_id.rs
@@ -1,4 +1,3 @@
-use serde_json::builder::ObjectBuilder;
use std::fmt::{Display, Formatter, Result as FmtResult};
use ::client::rest;
use ::internal::prelude::*;
@@ -71,10 +70,10 @@ impl GuildId {
/// [`rest::create_channel`]: ../client/rest/fn.create_channel.html
/// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
pub fn create_channel(&self, name: &str, kind: ChannelType) -> Result<GuildChannel> {
- let map = ObjectBuilder::new()
- .insert("name", name)
- .insert("type", kind.name())
- .build();
+ let map = json!({
+ "name": name,
+ "type": kind.name(),
+ });
rest::create_channel(self.0, &map)
}
@@ -97,10 +96,10 @@ impl GuildId {
/// [`utils::read_image`]: ../utils/fn.read_image.html
/// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
- let map = ObjectBuilder::new()
- .insert("name", name)
- .insert("image", image)
- .build();
+ let map = json!({
+ "name": name,
+ "image": image,
+ });
rest::create_emoji(self.0, &map)
}
@@ -113,10 +112,10 @@ impl GuildId {
pub fn create_integration<I>(&self, integration_id: I, kind: &str)
-> Result<()> where I: Into<IntegrationId> {
let integration_id = integration_id.into();
- let map = ObjectBuilder::new()
- .insert("id", integration_id.0)
- .insert("type", kind)
- .build();
+ let map = json!({
+ "id": integration_id.0,
+ "type": kind,
+ });
rest::create_guild_integration(self.0, integration_id.0, &map)
}
@@ -131,7 +130,7 @@ impl GuildId {
/// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
#[inline]
pub fn create_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
- rest::create_role(self.0, &f(EditRole::default()).0.build())
+ rest::create_role(self.0, &f(EditRole::default()).0)
}
/// Deletes the current guild if the current account is the owner of the
@@ -194,7 +193,7 @@ impl GuildId {
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[inline]
pub fn edit<F: FnOnce(EditGuild) -> EditGuild>(&mut self, f: F) -> Result<PartialGuild> {
- rest::edit_guild(self.0, &f(EditGuild::default()).0.build())
+ rest::edit_guild(self.0, &f(EditGuild::default()).0)
}
/// Edits an [`Emoji`]'s name in the guild.
@@ -208,7 +207,9 @@ impl GuildId {
/// [`Emoji::edit`]: struct.Emoji.html#method.edit
/// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
- let map = ObjectBuilder::new().insert("name", name).build();
+ let map = json!({
+ "name": name,
+ });
rest::edit_emoji(self.0, emoji_id.into().0, &map)
}
@@ -229,7 +230,7 @@ impl GuildId {
#[inline]
pub fn edit_member<F, U>(&self, user_id: U, f: F) -> Result<()>
where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
- rest::edit_member(self.0, user_id.into().0, &f(EditMember::default()).0.build())
+ rest::edit_member(self.0, user_id.into().0, &f(EditMember::default()).0)
}
/// Edits the current user's nickname for the guild.
@@ -263,7 +264,7 @@ impl GuildId {
#[inline]
pub fn edit_role<F, R>(&self, role_id: R, f: F) -> Result<Role>
where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
- rest::edit_role(self.0, role_id.into().0, &f(EditRole::default()).0.build())
+ rest::edit_role(self.0, role_id.into().0, &f(EditRole::default()).0)
}
/// Search the cache for the guild.
@@ -372,7 +373,9 @@ impl GuildId {
/// [`Member`]: struct.Member.html
/// [Kick Members]: permissions/constant.KICK_MEMBERS.html
pub fn get_prune_count(&self, days: u16) -> Result<GuildPrune> {
- let map = ObjectBuilder::new().insert("days", days).build();
+ let map = json!({
+ "days": days,
+ });
rest::get_guild_prune_count(self.0, &map)
}
@@ -411,7 +414,8 @@ impl GuildId {
/// [Move Members]: permissions/constant.MOVE_MEMBERS.html
pub fn move_member<C, U>(&self, user_id: U, channel_id: C)
-> Result<()> where C: Into<ChannelId>, U: Into<UserId> {
- let map = ObjectBuilder::new().insert("channel_id", channel_id.into().0).build();
+ let mut map = Map::new();
+ map.insert("channel_id".to_owned(), Value::Number(Number::from(channel_id.into().0)));
rest::edit_member(self.0, user_id.into().0, &map)
}
@@ -437,7 +441,11 @@ impl GuildId {
/// [Kick Members]: permissions/constant.KICK_MEMBERS.html
#[inline]
pub fn start_prune(&self, days: u16) -> Result<GuildPrune> {
- rest::start_guild_prune(self.0, &ObjectBuilder::new().insert("days", days).build())
+ let map = json!({
+ "days": days,
+ });
+
+ rest::start_guild_prune(self.0, &map)
}
/// Unbans a [`User`] from the guild.
diff --git a/src/model/guild/integration.rs b/src/model/guild/integration.rs
index d7f9967..b276bc3 100644
--- a/src/model/guild/integration.rs
+++ b/src/model/guild/integration.rs
@@ -1,4 +1,21 @@
-use ::model::{Integration, IntegrationId};
+use super::*;
+
+/// Various information about integrations.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Integration {
+ pub id: IntegrationId,
+ pub account: IntegrationAccount,
+ pub enabled: bool,
+ #[serde(rename="expire_behaviour")]
+ pub expire_behaviour: u64,
+ pub expire_grace_period: u64,
+ pub kind: String,
+ pub name: String,
+ pub role_id: RoleId,
+ pub synced_at: u64,
+ pub syncing: bool,
+ pub user: User,
+}
impl From<Integration> for IntegrationId {
/// Gets the Id of integration.
@@ -6,3 +23,10 @@ impl From<Integration> for IntegrationId {
integration.id
}
}
+
+/// Integration account object.
+#[derive(Clone, Debug, Deserialize)]
+pub struct IntegrationAccount {
+ pub id: String,
+ pub name: String,
+}
diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs
index 8fc53e1..630610f 100644
--- a/src/model/guild/member.rs
+++ b/src/model/guild/member.rs
@@ -1,15 +1,39 @@
use std::borrow::Cow;
use std::fmt::{Display, Formatter, Result as FmtResult};
-use ::internal::prelude::*;
+use super::deserialize_sync_user;
use ::model::*;
#[cfg(feature="cache")]
use ::client::{CACHE, rest};
#[cfg(feature="cache")]
+use ::internal::prelude::*;
+#[cfg(feature="cache")]
use ::utils::builder::EditMember;
#[cfg(feature="cache")]
use ::utils::Colour;
+/// Information about a member of a guild.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Member {
+ /// Indicator of whether the member can hear in voice channels.
+ pub deaf: bool,
+ /// The unique Id of the guild that the member is a part of.
+ pub guild_id: Option<GuildId>,
+ /// Timestamp representing the date when the member joined.
+ pub joined_at: String,
+ /// Indicator of whether the member can speak in voice channels.
+ pub mute: bool,
+ /// The member's nickname, if present.
+ ///
+ /// Can't be longer than 32 characters.
+ pub nick: Option<String>,
+ /// Vector of Ids of [`Role`]s given to the member.
+ pub roles: Vec<RoleId>,
+ /// Attached User struct.
+ #[serde(deserialize_with="deserialize_sync_user")]
+ pub user: Arc<RwLock<User>>,
+}
+
impl Member {
/// Adds a [`Role`] to the member, editing its roles in-place if the request
/// was successful.
@@ -50,7 +74,7 @@ impl Member {
let guild_id = self.find_guild()?;
self.roles.extend_from_slice(role_ids);
- let map = EditMember::default().roles(&self.roles).0.build();
+ let map = EditMember::default().roles(&self.roles).0;
match rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map) {
Ok(()) => Ok(()),
@@ -105,15 +129,6 @@ impl Member {
roles.iter().find(|r| r.colour.0 != default.0).map(|r| r.colour)
}
- #[doc(hidden)]
- pub fn decode_guild(guild_id: GuildId, mut value: Value) -> Result<Member> {
- if let Some(v) = value.as_object_mut() {
- v.insert("guild_id".to_owned(), Value::U64(guild_id.0));
- }
-
- Self::decode(value)
- }
-
/// Calculates the member's display name.
///
/// The nickname takes priority over the member's username if it exists.
@@ -141,7 +156,7 @@ impl Member {
#[cfg(feature="cache")]
pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) -> Result<()> {
let guild_id = self.find_guild()?;
- let map = f(EditMember::default()).0.build();
+ let map = f(EditMember::default()).0;
rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map)
}
@@ -210,7 +225,7 @@ impl Member {
let guild_id = self.find_guild()?;
self.roles.retain(|r| !role_ids.contains(r));
- let map = EditMember::default().roles(&self.roles).0.build();
+ let map = EditMember::default().roles(&self.roles).0;
match rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map) {
Ok(()) => Ok(()),
diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs
index 8b21ac7..7299f84 100644
--- a/src/model/guild/mod.rs
+++ b/src/model/guild/mod.rs
@@ -1,13 +1,5 @@
-use serde_json::builder::ObjectBuilder;
-use ::client::rest;
-use ::constants::LARGE_THRESHOLD;
-use ::model::*;
-use ::utils::builder::{EditGuild, EditMember, EditRole};
-
-#[cfg(feature="cache")]
-use ::client::CACHE;
-
mod emoji;
+mod feature;
mod guild_id;
mod integration;
mod member;
@@ -15,12 +7,117 @@ mod partial_guild;
mod role;
pub use self::emoji::*;
+pub use self::feature::*;
pub use self::guild_id::*;
pub use self::integration::*;
pub use self::member::*;
pub use self::partial_guild::*;
pub use self::role::*;
+use serde::de::Error as DeError;
+use serde_json;
+use super::utils::*;
+use ::client::rest;
+use ::constants::LARGE_THRESHOLD;
+use ::model::*;
+use ::utils::builder::{EditGuild, EditMember, EditRole};
+
+#[cfg(feature="cache")]
+use ::client::CACHE;
+
+/// A representation of a banning of a user.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Ban {
+ /// The reason given for this ban.
+ ///
+ /// **Note**: Until the Audit Log feature is completed by Discord, this will
+ /// always be `None`.
+ pub reason: Option<String>,
+ /// The user that was banned.
+ pub user: User,
+}
+
+/// Information about a Discord guild, such as channels, emojis, etc.
+#[derive(Clone, Debug)]
+pub struct Guild {
+ /// Id of a voice channel that's considered the AFK channel.
+ pub afk_channel_id: Option<ChannelId>,
+ /// The amount of seconds a user can not show any activity in a voice
+ /// channel before being moved to an AFK channel -- if one exists.
+ pub afk_timeout: u64,
+ /// All voice and text channels contained within a guild.
+ ///
+ /// This contains all channels regardless of permissions (i.e. the ability
+ /// of the bot to read from or connect to them).
+ pub channels: HashMap<ChannelId, Arc<RwLock<GuildChannel>>>,
+ /// Indicator of whether notifications for all messages are enabled by
+ /// default in the guild.
+ pub default_message_notifications: u64,
+ /// All of the guild's custom emojis.
+ pub emojis: HashMap<EmojiId, Emoji>,
+ /// VIP features enabled for the guild. Can be obtained through the
+ /// [Discord Partnership] website.
+ ///
+ /// [Discord Partnership]: https://discordapp.com/partners
+ pub features: Vec<Feature>,
+ /// The hash of the icon used by the guild.
+ ///
+ /// In the client, this appears on the guild list on the left-hand side.
+ pub icon: Option<String>,
+ /// The unique Id identifying the guild.
+ ///
+ /// This is equivilant to the Id of the default role (`@everyone`) and also
+ /// that of the default channel (typically `#general`).
+ pub id: GuildId,
+ /// The date that the current user joined the guild.
+ pub joined_at: String,
+ /// Indicator of whether the guild is considered "large" by Discord.
+ pub large: bool,
+ /// The number of members in the guild.
+ pub member_count: u64,
+ /// Users who are members of the guild.
+ ///
+ /// Members might not all be available when the [`ReadyEvent`] is received
+ /// if the [`member_count`] is greater than the `LARGE_THRESHOLD` set by
+ /// the library.
+ ///
+ /// [`ReadyEvent`]: events/struct.ReadyEvent.html
+ pub members: HashMap<UserId, Member>,
+ /// Indicator of whether the guild requires multi-factor authentication for
+ /// [`Role`]s or [`User`]s with moderation permissions.
+ ///
+ /// [`Role`]: struct.Role.html
+ /// [`User`]: struct.User.html
+ pub mfa_level: u64,
+ /// The name of the guild.
+ pub name: String,
+ /// The Id of the [`User`] who owns the guild.
+ ///
+ /// [`User`]: struct.User.html
+ pub owner_id: UserId,
+ /// A mapping of [`User`]s' Ids to their current presences.
+ ///
+ /// [`User`]: struct.User.html
+ pub presences: HashMap<UserId, Presence>,
+ /// The region that the voice servers that the guild uses are located in.
+ pub region: String,
+ /// A mapping of the guild's roles.
+ pub roles: HashMap<RoleId, Role>,
+ /// An identifying hash of the guild's splash icon.
+ ///
+ /// If the [`InviteSplash`] feature is enabled, this can be used to generate
+ /// a URL to a splash image.
+ ///
+ /// [`InviteSplash`]: enum.Feature.html#variant.InviteSplash
+ pub splash: Option<String>,
+ /// Indicator of the current verification level of the guild.
+ pub verification_level: VerificationLevel,
+ /// A mapping of of [`User`]s to their current voice state.
+ ///
+ /// [`User`]: struct.User.html
+ pub voice_states: HashMap<UserId, VoiceState>,
+}
+
impl Guild {
#[cfg(feature="cache")]
fn has_perms(&self, mut permissions: Permissions) -> Result<bool> {
@@ -133,11 +230,11 @@ impl Guild {
/// [US West region]: enum.Region.html#variant.UsWest
/// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild
pub fn create(name: &str, region: Region, icon: Option<&str>) -> Result<PartialGuild> {
- let map = ObjectBuilder::new()
- .insert("icon", icon)
- .insert("name", name)
- .insert("region", region.name())
- .build();
+ let map = json!({
+ "icon": icon,
+ "name": name,
+ "region": region.name(),
+ });
rest::create_guild(&map)
}
@@ -249,50 +346,6 @@ impl Guild {
self.id.create_role(f)
}
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Guild> {
- let mut map = into_map(value)?;
-
- let id = remove(&mut map, "id").and_then(GuildId::decode)?;
-
- let channels = {
- let mut channels = HashMap::new();
-
- let vals = decode_array(remove(&mut map, "channels")?,
- |v| GuildChannel::decode_guild(v, id))?;
-
- for channel in vals {
- channels.insert(channel.id, Arc::new(RwLock::new(channel)));
- }
-
- channels
- };
-
- Ok(Guild {
- afk_channel_id: opt(&mut map, "afk_channel_id", ChannelId::decode)?,
- afk_timeout: req!(remove(&mut map, "afk_timeout")?.as_u64()),
- channels: channels,
- default_message_notifications: req!(remove(&mut map, "default_message_notifications")?.as_u64()),
- emojis: remove(&mut map, "emojis").and_then(decode_emojis)?,
- features: remove(&mut map, "features").and_then(|v| decode_array(v, Feature::decode_str))?,
- icon: opt(&mut map, "icon", into_string)?,
- id: id,
- joined_at: remove(&mut map, "joined_at").and_then(into_string)?,
- large: req!(remove(&mut map, "large")?.as_bool()),
- member_count: req!(remove(&mut map, "member_count")?.as_u64()),
- members: remove(&mut map, "members").and_then(decode_members)?,
- mfa_level: req!(remove(&mut map, "mfa_level")?.as_u64()),
- name: remove(&mut map, "name").and_then(into_string)?,
- owner_id: remove(&mut map, "owner_id").and_then(UserId::decode)?,
- presences: remove(&mut map, "presences").and_then(decode_presences)?,
- region: remove(&mut map, "region").and_then(into_string)?,
- roles: remove(&mut map, "roles").and_then(decode_roles)?,
- splash: opt(&mut map, "splash", into_string)?,
- verification_level: remove(&mut map, "verification_level").and_then(VerificationLevel::decode)?,
- voice_states: remove(&mut map, "voice_states").and_then(decode_voice_states)?,
- })
- }
-
/// Deletes the current guild if the current user is the owner of the
/// guild.
///
@@ -904,6 +957,172 @@ impl Guild {
}
}
+impl Deserialize for Guild {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ let mut map = JsonMap::deserialize(deserializer)?;
+
+ let id = map.get("id")
+ .and_then(|x| x.as_str())
+ .and_then(|x| x.parse::<u64>().ok());
+
+ if let Some(guild_id) = id {
+ if let Some(array) = map.get_mut("channels").and_then(|x| x.as_array_mut()) {
+
+ for value in array {
+ if let Some(channel) = value.as_object_mut() {
+ channel.insert("guild_id".to_owned(), Value::Number(Number::from(guild_id)));
+ }
+ }
+ }
+ }
+
+ let afk_channel_id = match map.remove("afk_channel_id") {
+ Some(v) => serde_json::from_value::<Option<ChannelId>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let afk_timeout = map.remove("afk_timeout")
+ .ok_or_else(|| DeError::custom("expected guild afk_timeout"))
+ .and_then(u64::deserialize)
+ .map_err(DeError::custom)?;
+ let channels = map.remove("channels")
+ .ok_or_else(|| DeError::custom("expected guild channels"))
+ .and_then(deserialize_guild_channels)
+ .map_err(DeError::custom)?;
+ let default_message_notifications = map.remove("default_message_notifications")
+ .ok_or_else(|| DeError::custom("expected guild default_message_notifications"))
+ .and_then(u64::deserialize)
+ .map_err(DeError::custom)?;
+ let emojis = map.remove("emojis")
+ .ok_or_else(|| DeError::custom("expected guild emojis"))
+ .and_then(deserialize_emojis)
+ .map_err(DeError::custom)?;
+ let features = map.remove("features")
+ .ok_or_else(|| DeError::custom("expected guild features"))
+ .and_then(serde_json::from_value::<Vec<Feature>>)
+ .map_err(DeError::custom)?;
+ let icon = match map.remove("icon") {
+ Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let id = map.remove("id")
+ .ok_or_else(|| DeError::custom("expected guild id"))
+ .and_then(GuildId::deserialize)
+ .map_err(DeError::custom)?;
+ let joined_at = map.remove("joined_at")
+ .ok_or_else(|| DeError::custom("expected guild joined_at"))
+ .and_then(String::deserialize)
+ .map_err(DeError::custom)?;
+ let large = map.remove("large")
+ .ok_or_else(|| DeError::custom("expected guild large"))
+ .and_then(bool::deserialize)
+ .map_err(DeError::custom)?;
+ let member_count = map.remove("member_count")
+ .ok_or_else(|| DeError::custom("expected guild member_count"))
+ .and_then(u64::deserialize)
+ .map_err(DeError::custom)?;
+ let members = map.remove("members")
+ .ok_or_else(|| DeError::custom("expected guild members"))
+ .and_then(deserialize_members)
+ .map_err(DeError::custom)?;
+ let mfa_level = map.remove("mfa_level")
+ .ok_or_else(|| DeError::custom("expected guild mfa_level"))
+ .and_then(u64::deserialize)
+ .map_err(DeError::custom)?;
+ let name = map.remove("name")
+ .ok_or_else(|| DeError::custom("expected guild name"))
+ .and_then(String::deserialize)
+ .map_err(DeError::custom)?;
+ let owner_id = map.remove("owner_id")
+ .ok_or_else(|| DeError::custom("expected guild owner_id"))
+ .and_then(UserId::deserialize)
+ .map_err(DeError::custom)?;
+ let presences = map.remove("presences")
+ .ok_or_else(|| DeError::custom("expected guild presences"))
+ .and_then(deserialize_presences)
+ .map_err(DeError::custom)?;
+ let region = map.remove("region")
+ .ok_or_else(|| DeError::custom("expected guild region"))
+ .and_then(String::deserialize)
+ .map_err(DeError::custom)?;
+ let roles = map.remove("roles")
+ .ok_or_else(|| DeError::custom("expected guild roles"))
+ .and_then(deserialize_roles)
+ .map_err(DeError::custom)?;
+ let splash = match map.remove("splash") {
+ Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let verification_level = map.remove("verification_level")
+ .ok_or_else(|| DeError::custom("expected guild verification_level"))
+ .and_then(VerificationLevel::deserialize)
+ .map_err(DeError::custom)?;
+ let voice_states = map.remove("voice_states")
+ .ok_or_else(|| DeError::custom("expected guild voice_states"))
+ .and_then(deserialize_voice_states)
+ .map_err(DeError::custom)?;
+
+ Ok(Self {
+ afk_channel_id: afk_channel_id,
+ afk_timeout: afk_timeout,
+ channels: channels,
+ default_message_notifications: default_message_notifications,
+ emojis: emojis,
+ features: features,
+ icon: icon,
+ id: id,
+ joined_at: joined_at,
+ large: large,
+ member_count: member_count,
+ members: members,
+ mfa_level: mfa_level,
+ name: name,
+ owner_id: owner_id,
+ presences: presences,
+ region: region,
+ roles: roles,
+ splash: splash,
+ verification_level: verification_level,
+ voice_states: voice_states,
+ })
+ }
+}
+
+/// Information relating to a guild's widget embed.
+#[derive(Clone, Debug, Deserialize)]
+pub struct GuildEmbed {
+ /// The Id of the channel to show the embed for.
+ pub channel_id: ChannelId,
+ /// Whether the widget embed is enabled.
+ pub enabled: bool,
+}
+
+/// Representation of the number of members that would be pruned by a guild
+/// prune operation.
+#[derive(Clone, Copy, Debug, Deserialize)]
+pub struct GuildPrune {
+ /// The number of members that would be pruned by the operation.
+ pub pruned: u64,
+}
+
+/// Basic information about a guild.
+#[derive(Clone, Debug, Deserialize)]
+pub struct GuildInfo {
+ /// The unique Id of the guild.
+ ///
+ /// Can be used to calculate creation date.
+ pub id: GuildId,
+ /// The hash of the icon of the guild.
+ ///
+ /// This can be used to generate a URL to the guild's icon image.
+ pub icon: Option<String>,
+ /// The name of the guild.
+ pub name: String,
+ /// Indicator of whether the current user is the owner.
+ pub owner: bool,
+ /// The permissions that the current user has.
+ pub permissions: Permissions,
+}
+
impl GuildInfo {
/// Returns the formatted URL of the guild's icon, if the guild has an icon.
pub fn icon_url(&self) -> Option<String> {
@@ -938,46 +1157,65 @@ impl InviteGuild {
}
}
-impl PossibleGuild<Guild> {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Self> {
- let mut value = into_map(value)?;
- if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) {
- remove(&mut value, "id").and_then(GuildId::decode).map(PossibleGuild::Offline)
- } else {
- Guild::decode(Value::Object(value)).map(PossibleGuild::Online)
- }
- }
+/// Data for an unavailable guild.
+#[derive(Clone, Copy, Debug, Deserialize)]
+pub struct GuildUnavailable {
+ /// The Id of the [`Guild`] that is unavailable.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ pub id: GuildId,
+ /// Indicator of whether the guild is unavailable.
+ ///
+ /// This should always be `true`.
+ pub unavailable: bool,
+}
+#[allow(large_enum_variant)]
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
+pub enum GuildStatus {
+ OnlinePartialGuild(PartialGuild),
+ OnlineGuild(Guild),
+ Offline(GuildUnavailable),
+}
+
+impl GuildStatus {
/// Retrieves the Id of the inner [`Guild`].
///
/// [`Guild`]: struct.Guild.html
pub fn id(&self) -> GuildId {
match *self {
- PossibleGuild::Offline(guild_id) => guild_id,
- PossibleGuild::Online(ref live_guild) => live_guild.id,
+ GuildStatus::Offline(offline) => offline.id,
+ GuildStatus::OnlineGuild(ref guild) => guild.id,
+ GuildStatus::OnlinePartialGuild(ref partial_guild) => partial_guild.id,
}
}
}
-impl PossibleGuild<PartialGuild> {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Self> {
- let mut value = into_map(value)?;
- if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) {
- remove(&mut value, "id").and_then(GuildId::decode).map(PossibleGuild::Offline)
- } else {
- PartialGuild::decode(Value::Object(value)).map(PossibleGuild::Online)
- }
+enum_number!(
+ #[doc="The level to set as criteria prior to a user being able to send
+ messages in a [`Guild`].
+
+ [`Guild`]: struct.Guild.html"]
+ VerificationLevel {
+ /// Does not require any verification.
+ None = 0,
+ /// Low verification level.
+ Low = 1,
+ /// Medium verification level.
+ Medium = 2,
+ /// High verification level.
+ High = 3,
}
+);
- /// Retrieves the Id of the inner [`Guild`].
- ///
- /// [`Guild`]: struct.Guild.html
- pub fn id(&self) -> GuildId {
+impl VerificationLevel {
+ pub fn num(&self) -> u64 {
match *self {
- PossibleGuild::Offline(id) => id,
- PossibleGuild::Online(ref live_guild) => live_guild.id,
+ VerificationLevel::None => 0,
+ VerificationLevel::Low => 1,
+ VerificationLevel::Medium => 2,
+ VerificationLevel::High => 3,
}
}
}
diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs
index f5ab502..947de50 100644
--- a/src/model/guild/partial_guild.rs
+++ b/src/model/guild/partial_guild.rs
@@ -1,6 +1,30 @@
use ::model::*;
use ::utils::builder::{EditGuild, EditMember, EditRole};
+/// Partial information about a [`Guild`]. This does not include information
+/// like member data.
+///
+/// [`Guild`]: struct.Guild.html
+#[derive(Clone, Debug, Deserialize)]
+pub struct PartialGuild {
+ pub id: GuildId,
+ pub afk_channel_id: Option<ChannelId>,
+ pub afk_timeout: u64,
+ pub default_message_notifications: u64,
+ pub embed_channel_id: Option<ChannelId>,
+ pub embed_enabled: bool,
+ pub emojis: HashMap<EmojiId, Emoji>,
+ pub features: Vec<Feature>,
+ pub icon: Option<String>,
+ pub mfa_level: u64,
+ pub name: String,
+ pub owner_id: UserId,
+ pub region: String,
+ pub roles: HashMap<RoleId, Role>,
+ pub splash: Option<String>,
+ pub verification_level: VerificationLevel,
+}
+
impl PartialGuild {
/// Ban a [`User`] from the guild. All messages by the
/// user within the last given number of days given will be deleted. This
diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs
index 77d84e1..d4e1da8 100644
--- a/src/model/guild/role.rs
+++ b/src/model/guild/role.rs
@@ -9,6 +9,52 @@ use ::internal::prelude::*;
#[cfg(feature="cache")]
use ::utils::builder::EditRole;
+/// Information about a role within a guild. A role represents a set of
+/// permissions, and can be attached to one or multiple users. A role has
+/// various miscellaneous configurations, such as being assigned a colour. Roles
+/// are unique per guild and do not cross over to other guilds in any way, and
+/// can have channel-specific permission overrides in addition to guild-level
+/// permissions.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Role {
+ /// The Id of the role. Can be used to calculate the role's creation date.
+ pub id: RoleId,
+ /// The colour of the role. This is an ergonomic representation of the inner
+ /// value.
+ #[serde(rename="color")]
+ pub colour: Colour,
+ /// Indicator of whether the role is pinned above lesser roles.
+ ///
+ /// In the client, this causes [`Member`]s in the role to be seen above
+ /// those in roles with a lower [`position`].
+ ///
+ /// [`Member`]: struct.Member.html
+ /// [`position`]: #structfield.position
+ pub hoist: bool,
+ /// Indicator of whether the role is managed by an integration service.
+ pub managed: bool,
+ /// Indicator of whether the role can be mentioned, similar to mentioning a
+ /// specific member or `@everyone`.
+ ///
+ /// Only members of the role will be notified if a role is mentioned with
+ /// this set to `true`.
+ #[serde(default)]
+ pub mentionable: bool,
+ /// The name of the role.
+ pub name: String,
+ /// A set of permissions that the role has been assigned.
+ ///
+ /// See the [`permissions`] module for more information.
+ ///
+ /// [`permissions`]: permissions/index.html
+ pub permissions: Permissions,
+ /// The role's position in the position list. Roles are considered higher in
+ /// hierarchy if their position is higher.
+ ///
+ /// The `@everyone` role is usually either `-1` or `0`.
+ pub position: i64,
+}
+
impl Role {
/// Deletes the role.
///
diff --git a/src/model/invite.rs b/src/model/invite.rs
index 4e33a3f..d041d55 100644
--- a/src/model/invite.rs
+++ b/src/model/invite.rs
@@ -1,7 +1,6 @@
-use super::{Invite, RichInvite};
+use super::*;
use ::client::rest;
use ::internal::prelude::*;
-use ::model::ChannelId;
use ::utils::builder::CreateInvite;
use ::utils;
@@ -10,6 +9,25 @@ use super::permissions;
#[cfg(feature="cache")]
use super::utils as model_utils;
+/// Information about an invite code.
+///
+/// Information can not be accessed for guilds the current user is banned from.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Invite {
+ /// The unique code for the invite.
+ pub code: String,
+ /// A representation of the minimal amount of information needed about the
+ /// [`GuildChannel`] being invited to.
+ ///
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ pub channel: InviteChannel,
+ /// a representation of the minimal amount of information needed about the
+ /// [`Guild`] being invited to.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ pub guild: InviteGuild,
+}
+
impl Invite {
/// Creates an invite for a [`GuildChannel`], providing a builder so that
/// fields may optionally be set.
@@ -42,7 +60,7 @@ impl Invite {
}
}
- rest::create_invite(channel_id.0, &f(CreateInvite::default()).0.build())
+ rest::create_invite(channel_id.0, &f(CreateInvite::default()).0)
}
/// Deletes the invite.
@@ -76,6 +94,65 @@ impl Invite {
}
}
+/// A inimal information about the channel an invite points to.
+#[derive(Clone, Debug, Deserialize)]
+pub struct InviteChannel {
+ pub id: ChannelId,
+ pub name: String,
+ #[serde(rename="type")]
+ pub kind: ChannelType,
+}
+
+/// A minimal amount of information about the guild an invite points to.
+#[derive(Clone, Debug, Deserialize)]
+pub struct InviteGuild {
+ pub id: GuildId,
+ pub icon: Option<String>,
+ pub name: String,
+ pub splash_hash: Option<String>,
+}
+
+/// Detailed information about an invite.
+/// This information can only be retrieved by anyone with the [Manage Guild]
+/// permission. Otherwise, a minimal amount of information can be retrieved via
+/// the [`Invite`] struct.
+///
+/// [`Invite`]: struct.Invite.html
+/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+#[derive(Clone, Debug, Deserialize)]
+pub struct RichInvite {
+ /// A representation of the minimal amount of information needed about the
+ /// channel being invited to.
+ pub channel: InviteChannel,
+ /// The unique code for the invite.
+ pub code: String,
+ /// When the invite was created.
+ pub created_at: String,
+ /// A representation of the minimal amount of information needed about the
+ /// guild being invited to.
+ pub guild: InviteGuild,
+ /// The user that created the invite.
+ pub inviter: User,
+ /// The maximum age of the invite in seconds, from when it was created.
+ pub max_age: u64,
+ /// The maximum number of times that an invite may be used before it expires.
+
+ /// Note that this does not supercede the [`max_age`] value, if the value of
+ /// [`temporary`] is `true`. If the value of `temporary` is `false`, then the
+ /// invite _will_ self-expire after the given number of max uses.
+
+ /// If the value is `0`, then the invite is permanent.
+ ///
+ /// [`max_age`]: #structfield.max_age
+ /// [`temporary`]: #structfield.temporary
+ pub max_uses: u64,
+ /// Indicator of whether the invite self-expires after a certain amount of
+ /// time or uses.
+ pub temporary: bool,
+ /// The amount of times that an invite has been used.
+ pub uses: u64,
+}
+
impl RichInvite {
/// Deletes the invite.
///
diff --git a/src/model/misc.rs b/src/model/misc.rs
index 118492c..5cf3a91 100644
--- a/src/model/misc.rs
+++ b/src/model/misc.rs
@@ -1,18 +1,6 @@
-use super::{
- ChannelId,
- Channel,
- Emoji,
- Member,
- RoleId,
- Role,
- UserId,
- User,
- IncidentStatus,
- EmojiIdentifier
-};
-use ::internal::prelude::*;
-use std::str::FromStr;
use std::result::Result as StdResult;
+use std::str::FromStr;
+use super::*;
use ::utils;
/// Allows something - such as a channel or role - to be mentioned in a message.
@@ -130,6 +118,16 @@ impl FromStr for RoleId {
}
}
+/// A version of an emoji used only when solely the Id and name are known.
+#[derive(Clone, Debug)]
+pub struct EmojiIdentifier {
+ /// The Id of the emoji.
+ pub id: EmojiId,
+ /// The name of the emoji. It must be at least 2 characters long and can
+ /// only contain alphanumeric characters and underscores.
+ pub name: String,
+}
+
impl EmojiIdentifier {
/// Generates a URL to the emoji's image.
#[inline]
@@ -171,9 +169,66 @@ impl FromStr for Channel {
}
}
-impl IncidentStatus {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Self> {
- Self::decode_str(value)
- }
+/// A component that was affected during a service incident.
+///
+/// This is pulled from the Discord status page.
+#[derive(Clone, Debug, Deserialize)]
+pub struct AffectedComponent {
+ pub name: String,
+}
+
+/// An incident retrieved from the Discord status page.
+///
+/// This is not necessarily a representation of an ongoing incident.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Incident {
+ pub created_at: String,
+ pub id: String,
+ pub impact: String,
+ pub incident_updates: Vec<IncidentUpdate>,
+ pub monitoring_at: Option<String>,
+ pub name: String,
+ pub page_id: String,
+ pub resolved_at: Option<String>,
+ pub short_link: String,
+ pub status: String,
+ pub updated_at: String,
+}
+
+/// An update to an incident from the Discord status page.
+///
+/// This will typically state what new information has been discovered about an
+/// incident.
+#[derive(Clone, Debug, Deserialize)]
+pub struct IncidentUpdate {
+ pub affected_components: Vec<AffectedComponent>,
+ pub body: String,
+ pub created_at: String,
+ pub display_at: String,
+ pub id: String,
+ pub incident_id: String,
+ pub status: IncidentStatus,
+ pub updated_at: String,
+}
+
+/// The type of status update during a service incident.
+#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+#[serde(rename_all="snake_case")]
+pub enum IncidentStatus {
+ Identified,
+ Investigating,
+ Monitoring,
+ Postmortem,
+ Resolved,
+}
+
+/// A Discord status maintenance message. This can be either for active
+/// maintenances or for scheduled maintenances.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Maintenance {
+ pub description: String,
+ pub id: String,
+ pub name: String,
+ pub start: String,
+ pub stop: String,
}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index ad70076..9b15049 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -25,6 +25,7 @@ mod guild;
mod invite;
mod misc;
mod user;
+mod voice;
mod webhook;
pub use self::channel::*;
@@ -34,6 +35,7 @@ pub use self::invite::*;
pub use self::misc::*;
pub use self::permissions::Permissions;
pub use self::user::*;
+pub use self::voice::*;
pub use self::webhook::*;
use self::utils::*;
@@ -41,40 +43,23 @@ use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use time::Timespec;
use ::internal::prelude::*;
-use ::utils::{Colour, decode_array};
-
-// All of the enums and structs are imported here. These are built from the
-// build script located at `./build.rs`.
-//
-// These use definitions located in `./definitions`, to map to structs and
-// enums, each respectively located in their own folder.
-//
-// For structs, this will almost always include their decode method, although
-// some require their own decoding due to many special fields.
-//
-// For enums, this will include the variants, and will automatically generate
-// the number/string decoding methods where appropriate.
-//
-// As only the struct/enum itself and common mappings can be built, this leaves
-// unique methods on each to be implemented here.
-include!(concat!(env!("OUT_DIR"), "/models/built.rs"));
+use ::utils::Colour;
+
+fn default_true() -> bool { true }
macro_rules! id {
($(#[$attr:meta] $name:ident;)*) => {
$(
#[$attr]
- #[derive(Copy, Clone, Debug, Eq, Hash, PartialOrd, Ord)]
+ #[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialOrd, Ord, Serialize)]
#[allow(derive_hash_xor_eq)]
pub struct $name(pub u64);
impl $name {
- fn decode(value: Value) -> Result<Self> {
- decode_id(value).map($name)
- }
-
/// Retrieves the time that the Id was created at.
pub fn created_at(&self) -> Timespec {
let offset = (self.0 >> 22) / 1000;
+
Timespec::new(1420070400 + offset as i64, 0)
}
}
@@ -119,24 +104,6 @@ id! {
WebhookId;
}
-/// A container for any channel.
-#[derive(Clone, Debug)]
-pub enum Channel {
- /// A group. A group comprises of only one channel.
- Group(Arc<RwLock<Group>>),
- /// A [text] or [voice] channel within a [`Guild`].
- ///
- /// [`Guild`]: struct.Guild.html
- /// [text]: enum.ChannelType.html#variant.Text
- /// [voice]: enum.ChannelType.html#variant.Voice
- Guild(Arc<RwLock<GuildChannel>>),
- /// A private channel to another [`User`]. No other users may access the
- /// channel. For multi-user "private channels", use a group.
- ///
- /// [`User`]: struct.User.html
- Private(Arc<RwLock<PrivateChannel>>),
-}
-
/// A container for guilds.
///
/// This is used to differentiate whether a guild itself can be used or whether
@@ -149,29 +116,6 @@ pub enum GuildContainer {
Id(GuildId),
}
-/// The type of edit being made to a Channel's permissions.
-///
-/// This is for use with methods such as `Context::create_permission`.
-///
-/// [`Context::create_permission`]: ../client/
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum PermissionOverwriteType {
- /// A member which is having its permission overwrites edited.
- Member(UserId),
- /// A role which is having its permission overwrites edited.
- Role(RoleId),
-}
-
-/// A guild which may or may not currently be available.
-#[derive(Clone, Debug)]
-pub enum PossibleGuild<T> {
- /// An indicator that a guild is currently unavailable for at least one of
- /// a variety of reasons.
- Offline(GuildId),
- /// An indicator that a guild is currently available.
- Online(T),
-}
-
/// Denotes the target for a search.
///
/// This can be either a [`Guild`] - which can search multiple [`Channel`]s -
@@ -204,3 +148,145 @@ impl From<GuildId> for SearchTarget {
SearchTarget::Guild(guild_id)
}
}
+
+/// Information about a user's application. An application does not necessarily
+/// have an associated bot user.
+#[derive(Clone, Debug, Deserialize)]
+pub struct ApplicationInfo {
+ /// The bot user associated with the application. See [`BotApplication`] for
+ /// more information.
+ ///
+ /// [`BotApplication`]: struct.BotApplication.html
+ pub bot: Option<BotApplication>,
+ /// Indicator of whether the bot is public.
+ ///
+ /// If a bot is public, anyone may invite it to their [`Guild`]. While a bot
+ /// is private, only the owner may add it to a guild.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ #[serde(default="default_true")]
+ pub bot_public: bool,
+ /// Indicator of whether the bot requires an OAuth2 code grant.
+ pub bot_require_code_grant: bool,
+ /// A description of the application, assigned by the application owner.
+ pub description: String,
+ /// A set of bitflags assigned to the application, which represent gated
+ /// feature flags that have been enabled for the application.
+ pub flags: Option<u64>,
+ /// A hash pointing to the application's icon.
+ ///
+ /// This is not necessarily equivalent to the bot user's avatar.
+ pub icon: Option<String>,
+ /// The unique numeric Id of the application.
+ pub id: UserId,
+ /// The name assigned to the application by the application owner.
+ pub name: String,
+ /// A list of redirect URIs assigned to the application.
+ pub redirect_uris: Vec<String>,
+ /// A list of RPC Origins assigned to the application.
+ pub rpc_origins: Vec<String>,
+ /// The given secret to the application.
+ ///
+ /// This is not equivalent to the application's bot user's token.
+ pub secret: String,
+}
+
+/// Information about an application with an application's bot user.
+#[derive(Clone, Debug, Deserialize)]
+pub struct BotApplication {
+ /// The unique Id of the bot user.
+ pub id: UserId,
+ /// A hash of the avatar, if one is assigned.
+ ///
+ /// Can be used to generate a full URL to the avatar.
+ pub avatar: Option<String>,
+ /// Indicator of whether it is a bot.
+ #[serde(default)]
+ pub bot: bool,
+ /// The discriminator assigned to the bot user.
+ ///
+ /// While discriminators are not unique, the `username#discriminator` pair
+ /// is.
+ pub discriminator: u16,
+ /// The bot user's username.
+ pub name: String,
+ /// The token used to authenticate as the bot user.
+ ///
+ /// **Note**: Keep this information private, as untrusted sources can use it
+ /// to perform any action with a bot user.
+ pub token: String,
+}
+
+/// Information about the current application and its owner.
+#[derive(Clone, Debug, Deserialize)]
+pub struct CurrentApplicationInfo {
+ pub description: String,
+ pub icon: Option<String>,
+ pub id: UserId,
+ pub name: String,
+ pub owner: User,
+ #[serde(default)]
+ pub rpc_origins: Vec<String>,
+}
+
+/// The name of a region that a voice server can be located in.
+#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
+pub enum Region {
+ #[serde(rename="amsterdam")]
+ Amsterdam,
+ #[serde(rename="brazil")]
+ Brazil,
+ #[serde(rename="eu-central")]
+ EuCentral,
+ #[serde(rename="eu-west")]
+ EuWest,
+ #[serde(rename="frankfurt")]
+ Frankfurt,
+ #[serde(rename="london")]
+ London,
+ #[serde(rename="sydney")]
+ Sydney,
+ #[serde(rename="us-central")]
+ UsCentral,
+ #[serde(rename="us-east")]
+ UsEast,
+ #[serde(rename="us-south")]
+ UsSouth,
+ #[serde(rename="us-west")]
+ UsWest,
+ #[serde(rename="vip-amsterdam")]
+ VipAmsterdam,
+ #[serde(rename="vip-us-east")]
+ VipUsEast,
+ #[serde(rename="vip-us-west")]
+ VipUsWest,
+}
+
+impl Region {
+ pub fn name(&self) -> &str {
+ match *self {
+ Region::Amsterdam => "amsterdam",
+ Region::Brazil => "brazil",
+ Region::EuCentral => "eu-central",
+ Region::EuWest => "eu-west",
+ Region::Frankfurt => "frankfurt",
+ Region::London => "london",
+ Region::Sydney => "sydney",
+ Region::UsCentral => "us-central",
+ Region::UsEast => "us-east",
+ Region::UsSouth => "us-south",
+ Region::UsWest => "us-west",
+ Region::VipAmsterdam => "vip-amsterdam",
+ Region::VipUsEast => "vip-us-east",
+ Region::VipUsWest => "vip-us-west",
+ }
+ }
+}
+
+use serde::{Deserialize, Deserializer};
+use std::result::Result as StdResult;
+
+fn deserialize_sync_user<D: Deserializer>(deserializer: D)
+ -> StdResult<Arc<RwLock<User>>, D::Error> {
+ Ok(Arc::new(RwLock::new(User::deserialize(deserializer)?)))
+}
diff --git a/src/model/permissions.rs b/src/model/permissions.rs
index b3345eb..09fad4a 100644
--- a/src/model/permissions.rs
+++ b/src/model/permissions.rs
@@ -37,7 +37,10 @@
//! [Manage Roles]: constant.MANAGE_ROLES.html
//! [Manage Webhooks]: constant.MANAGE_WEBHOOKS.html
-use ::internal::prelude::*;
+use serde::de::{Error as DeError, Visitor};
+use serde::{Deserialize, Deserializer};
+use std::fmt::{Formatter, Result as FmtResult};
+use std::result::Result as StdResult;
/// Returns a set of permissions with the original @everyone permissions set
/// to true.
@@ -145,6 +148,7 @@ pub fn voice() -> Permissions {
CONNECT | SPEAK | USE_VAD
}
+
bitflags! {
/// A set of permissions that can be assigned to [`User`]s and [`Role`]s via
/// [`PermissionOverwrite`]s, roles globally in a [`Guild`], and to
@@ -253,11 +257,6 @@ bitflags! {
}
impl Permissions {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Permissions> {
- Ok(Self::from_bits_truncate(value.as_u64().unwrap()))
- }
-
/// Shorthand for checking that the set of permissions contains the
/// [Add Reactions] permission.
///
@@ -482,3 +481,35 @@ impl Permissions {
self.contains(self::USE_VAD)
}
}
+
+impl Deserialize for Permissions {
+ fn deserialize<D: Deserializer>(deserializer: D) -> StdResult<Self, D::Error> {
+ Ok(Permissions::from_bits_truncate(deserializer.deserialize_u64(U64Visitor)?))
+ }
+}
+
+struct U64Visitor;
+
+impl Visitor for U64Visitor {
+ type Value = u64;
+
+ fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
+ formatter.write_str("an unsigned 64-bit integer")
+ }
+
+ fn visit_i32<E: DeError>(self, value: i32) -> StdResult<u64, E> {
+ Ok(value as u64)
+ }
+
+ 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_u64<E: DeError>(self, value: u64) -> StdResult<u64, E> {
+ Ok(value)
+ }
+}
diff --git a/src/model/user.rs b/src/model/user.rs
index d77275e..b38756a 100644
--- a/src/model/user.rs
+++ b/src/model/user.rs
@@ -1,17 +1,6 @@
-use serde_json::builder::ObjectBuilder;
+use serde_json;
use std::{fmt, mem};
-use super::{
- CurrentUser,
- GuildContainer,
- GuildId,
- GuildInfo,
- Member,
- Message,
- PrivateChannel,
- RoleId,
- User,
- UserId,
-};
+use super::*;
use time::Timespec;
use ::client::rest::{self, GuildPagination};
use ::internal::prelude::*;
@@ -23,6 +12,56 @@ use std::sync::{Arc, RwLock};
#[cfg(feature="cache")]
use ::client::CACHE;
+/// An override for a channel.
+#[derive(Clone, Debug, Deserialize)]
+pub struct ChannelOverride {
+ /// The channel the override is for.
+ pub channel_id: ChannelId,
+ /// The notification level to use for the channel.
+ pub message_notifications: NotificationLevel,
+ /// Indicator of whether the channel is muted.
+ ///
+ /// In the client, this will not show an unread indicator for the channel,
+ /// although it will continue to show when the user is mentioned in it.
+ pub muted: bool,
+}
+
+/// The type of a user connection.
+///
+/// Note that this is related to a [`Connection`], and has nothing to do with
+/// WebSocket connections.
+///
+/// [`Connection`]: struct.Connection.html
+#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+pub enum ConnectionType {
+ /// A Battle.net connection.
+ #[serde(rename="battlenet")]
+ BattleNet,
+ /// A Steam connection.
+ #[serde(rename="steam")]
+ Steam,
+ /// A Twitch.tv connection.
+ #[serde(rename="twitch")]
+ TwitchTv,
+ #[serde(rename="youtube")]
+ YouTube,
+}
+
+/// Information about the current user.
+#[derive(Clone, Debug, Deserialize)]
+pub struct CurrentUser {
+ pub id: UserId,
+ pub avatar: Option<String>,
+ #[serde(default)]
+ pub bot: bool,
+ pub discriminator: u16,
+ pub email: Option<String>,
+ pub mfa_enabled: bool,
+ #[serde(rename="username")]
+ pub name: String,
+ pub verified: bool,
+}
+
impl CurrentUser {
/// Returns the formatted URL of the user's icon, if one exists.
///
@@ -65,15 +104,14 @@ impl CurrentUser {
/// ```
pub fn edit<F>(&mut self, f: F) -> Result<()>
where F: FnOnce(EditProfile) -> EditProfile {
- let mut map = ObjectBuilder::new()
- .insert("avatar", Some(&self.avatar))
- .insert("username", &self.name);
+ let mut map = Map::new();
+ map.insert("username".to_owned(), Value::String(self.name.clone()));
if let Some(email) = self.email.as_ref() {
- map = map.insert("email", email)
+ map.insert("email".to_owned(), Value::String(email.clone()));
}
- match rest::edit_profile(&f(EditProfile(map)).0.build()) {
+ match rest::edit_profile(&f(EditProfile(map)).0) {
Ok(new) => {
let _ = mem::replace(self, new);
@@ -97,6 +135,194 @@ impl CurrentUser {
}
}
+/// An enum that represents a default avatar.
+///
+/// The default avatar is calculated via the result of `discriminator % 5`.
+///
+/// The has of the avatar can be retrieved via calling [`name`] on the enum.
+///
+/// [`name`]: #method.name
+#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+pub enum DefaultAvatar {
+ /// The avatar when the result is `0`.
+ #[serde(rename="6debd47ed13483642cf09e832ed0bc1b")]
+ Blurple,
+ /// The avatar when the result is `1`.
+ #[serde(rename="322c936a8c8be1b803cd94861bdfa868")]
+ Grey,
+ /// The avatar when the result is `2`.
+ #[serde(rename="dd4dbc0016779df1378e7812eabaa04d")]
+ Green,
+ /// The avatar when the result is `3`.
+ #[serde(rename="0e291f67c9274a1abdddeb3fd919cbaa")]
+ Orange,
+ /// The avatar when the result is `4`.
+ #[serde(rename="1cbd08c76f8af6dddce02c5138971129")]
+ Red,
+}
+
+impl DefaultAvatar {
+ /// Retrieves the String hash of the default avatar.
+ pub fn name(&self) -> Result<String> {
+ serde_json::to_string(self).map_err(From::from)
+ }
+}
+
+/// Flags about who may add the current user as a friend.
+#[derive(Clone, Debug, Deserialize)]
+pub struct FriendSourceFlags {
+ #[serde(default)]
+ pub all: bool,
+ #[serde(default)]
+ pub mutual_friends: bool,
+ #[serde(default)]
+ pub mutual_guilds: bool,
+}
+
+enum_number!(
+ /// Identifier for the notification level of a channel.
+ NotificationLevel {
+ /// Receive notifications for everything.
+ All = 0,
+ /// Receive only mentions.
+ Mentions = 1,
+ /// Receive no notifications.
+ Nothing = 2,
+ /// Inherit the notification level from the parent setting.
+ Parent = 3,
+ }
+);
+
+/// The representation of a user's status.
+///
+/// # Examples
+///
+/// - [`DoNotDisturb`];
+/// - [`Invisible`].
+///
+/// [`DoNotDisturb`]: #variant.DoNotDisturb
+/// [`Invisible`]: #variant.Invisible
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
+pub enum OnlineStatus {
+ #[serde(rename="dnd")]
+ DoNotDisturb,
+ #[serde(rename="idle")]
+ Idle,
+ #[serde(rename="invisible")]
+ Invisible,
+ #[serde(rename="offline")]
+ Offline,
+ #[serde(rename="online")]
+ Online,
+}
+
+impl OnlineStatus {
+ pub fn name(&self) -> &str {
+ match *self {
+ OnlineStatus::DoNotDisturb => "dnd",
+ OnlineStatus::Idle => "idle",
+ OnlineStatus::Invisible => "invisible",
+ OnlineStatus::Offline => "offline",
+ OnlineStatus::Online => "online",
+ }
+ }
+}
+
+impl Default for OnlineStatus {
+ fn default() -> OnlineStatus {
+ OnlineStatus::Online
+ }
+}
+
+/// A summary of messages for a channel.
+///
+/// These are received within a [`ReadyEvent`].
+///
+/// [`ReadyEvent`]: event/struct.ReadyEvent.html
+#[derive(Clone, Debug, Deserialize)]
+pub struct ReadState {
+ /// The unique Id of the channel.
+ pub id: ChannelId,
+ /// The Id of the latest message sent to the channel.
+ pub last_message_id: Option<MessageId>,
+ /// The time that a message was most recently pinned to the channel.
+ pub last_pin_timestamp: Option<String>,
+ /// The amount of times that the current user has been mentioned in the
+ /// channel since the last message ACKed.
+ #[serde(default)]
+ pub mention_count: u64,
+}
+
+/// Information about a relationship that a user has with another user.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Relationship {
+ /// Unique Id of the other user.
+ pub id: UserId,
+ /// The type of the relationship, e.g. blocked, friends, etc.
+ #[serde(rename="type")]
+ pub kind: RelationshipType,
+ /// The User instance of the other user.
+ pub user: User,
+}
+
+enum_number!(
+ /// The type of relationship between the current user and another user.
+ RelationshipType {
+ /// The current user has a friend request ignored.
+ Ignored = 0,
+ /// The current user has the other user added as a friend.
+ Friends = 1,
+ /// The current user has the other blocked.
+ Blocked = 2,
+ /// The current user has an incoming friend request from the other user.
+ IncomingRequest = 3,
+ /// The current user has a friend request outgoing.
+ OutgoingRequest = 4,
+ }
+);
+
+/// A reason that a user was suggested to be added as a friend.
+#[derive(Clone, Debug, Deserialize)]
+pub struct SuggestionReason {
+ /// The name of the user on the platform.
+ pub name: String,
+ /// The type of reason.
+ pub kind: u64,
+ /// The platform that the current user and the other user share.
+ pub platform: ConnectionType,
+}
+
+/// The current user's progress through the Discord tutorial.
+///
+/// This is only applicable to selfbots.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Tutorial {
+ pub indicators_confirmed: Vec<String>,
+ pub indicators_suppressed: bool,
+}
+
+/// Information about a user.
+#[derive(Clone, Debug, Deserialize)]
+pub struct User {
+ /// The unique Id of the user. Can be used to calculate the account's
+ /// cration date.
+ pub id: UserId,
+ /// Optional avatar hash.
+ pub avatar: Option<String>,
+ /// Indicator of whether the user is a bot.
+ #[serde(default)]
+ pub bot: bool,
+ /// The account's discriminator to differentiate the user from others with
+ /// the same [`name`]. The name+discriminator pair is always unique.
+ ///
+ /// [`name`]: #structfield.name
+ pub discriminator: String,
+ /// The account's username. Changing username will trigger a discriminator
+ /// change if the username+discriminator pair becomes non-unique.
+ #[serde(rename="username")]
+ pub name: String,
+}
+
impl User {
/// Returns the formatted URL of the user's icon, if one exists.
///
@@ -202,24 +428,24 @@ impl User {
if let Some(finding) = finding {
finding
} else {
- let map = ObjectBuilder::new()
- .insert("recipient_id", self.id.0)
- .build();
+ let map = json!({
+ "recipient_id": self.id.0,
+ });
rest::create_private_channel(&map)?.id
}
} else {
- let map = ObjectBuilder::new()
- .insert("recipient_id", self.id.0)
- .build();
+ let map = json!({
+ "recipient_id": self.id.0,
+ });
rest::create_private_channel(&map)?.id
}};
- let map = ObjectBuilder::new()
- .insert("content", content)
- .insert("tts", false)
- .build();
+ let map = json!({
+ "content": content,
+ "tts": false,
+ });
rest::send_message(private_channel_id.0, &map)
}
@@ -319,13 +545,48 @@ impl fmt::Display for User {
}
}
+/// A user's connection.
+///
+/// **Note**: This is not in any way related to a WebSocket connection.
+#[derive(Clone, Debug, Deserialize)]
+pub struct UserConnection {
+ /// The User's Id through the connection.
+ pub id: String,
+ /// Whether the user automatically syncs friends through the connection.
+ pub friend_sync: bool,
+ /// The relevant integrations.
+ pub integrations: Vec<Integration>,
+ /// The type of connection set.
+ #[serde(rename="type")]
+ pub kind: ConnectionType,
+ /// The user's name through the connection.
+ pub name: String,
+ /// Indicator of whether the connection has been revoked.
+ pub revoked: bool,
+ /// The visibility level.
+ pub visibility: u64,
+}
+
+/// Settings about a guild in regards to notification configuration.
+#[derive(Clone, Debug, Deserialize)]
+pub struct UserGuildSettings {
+ pub channel_overriddes: Vec<ChannelOverride>,
+ pub guild_id: Option<GuildId>,
+ pub message_notifications: NotificationLevel,
+ pub mobile_push: bool,
+ pub muted: bool,
+ pub suppress_everyone: bool,
+}
+
impl UserId {
/// Creates a direct message channel between the [current user] and the
/// user. This can also retrieve the channel if one already exists.
///
/// [current user]: struct.CurrentUser.html
pub fn create_dm_channel(&self) -> Result<PrivateChannel> {
- let map = ObjectBuilder::new().insert("recipient_id", self.0).build();
+ let map = json!({
+ "recipient_id": self.0,
+ });
rest::create_private_channel(&map)
}
diff --git a/src/model/utils.rs b/src/model/utils.rs
index cbae244..ab7a8fb 100644
--- a/src/model/utils.rs
+++ b/src/model/utils.rs
@@ -1,69 +1,46 @@
-use std::collections::{BTreeMap, HashMap};
+use serde::de::Error as DeError;
+use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use super::*;
+
+#[cfg(feature="cache")]
use ::internal::prelude::*;
-use ::utils::{decode_array, into_array};
#[cfg(feature="cache")]
use super::permissions::{self, Permissions};
#[cfg(feature="cache")]
use ::client::CACHE;
-#[macro_escape]
-macro_rules! req {
- ($opt:expr) => {
- $opt.ok_or(Error::Decode(concat!("Type mismatch in model:",
- line!(),
- ": ",
- stringify!($opt)),
- Value::Null))?
- }
-}
-
-pub fn decode_emojis(value: Value) -> Result<HashMap<EmojiId, Emoji>> {
+pub fn deserialize_emojis<D: Deserializer>(deserializer: D)
+ -> StdResult<HashMap<EmojiId, Emoji>, D::Error> {
+ let vec: Vec<Emoji> = Deserialize::deserialize(deserializer)?;
let mut emojis = HashMap::new();
- for emoji in decode_array(value, Emoji::decode)? {
+ for emoji in vec {
emojis.insert(emoji.id, emoji);
}
Ok(emojis)
}
-pub fn decode_id(value: Value) -> Result<u64> {
- match value {
- Value::U64(num) => Ok(num),
- Value::I64(num) => Ok(num as u64),
- Value::String(text) => match text.parse::<u64>() {
- Ok(num) => Ok(num),
- Err(_) => Err(Error::Decode("Expected numeric ID",
- Value::String(text)))
- },
- value => Err(Error::Decode("Expected numeric ID", value))
- }
-}
+pub fn deserialize_guild_channels<D: Deserializer>(deserializer: D)
+ -> StdResult<HashMap<ChannelId, Arc<RwLock<GuildChannel>>>, D::Error> {
+ let vec: Vec<GuildChannel> = Deserialize::deserialize(deserializer)?;
+ let mut map = HashMap::new();
-pub fn decode_members(value: Value) -> Result<HashMap<UserId, Member>> {
- let mut members = HashMap::new();
-
- for member in decode_array(value, Member::decode)? {
- let user_id = member.user.read().unwrap().id;
-
- members.insert(user_id, member);
+ for channel in vec {
+ map.insert(channel.id, Arc::new(RwLock::new(channel)));
}
- Ok(members)
+ Ok(map)
}
-pub fn decode_guild_members(guild_id: GuildId, value: Value) -> Result<HashMap<UserId, Member>> {
+pub fn deserialize_members<D: Deserializer>(deserializer: D)
+ -> StdResult<HashMap<UserId, Member>, D::Error> {
+ let vec: Vec<Member> = Deserialize::deserialize(deserializer)?;
let mut members = HashMap::new();
- let member_vec = into_array(value).map(|x| x
- .into_iter()
- .map(|v| Member::decode_guild(guild_id, v))
- .filter_map(Result::ok)
- .collect::<Vec<_>>())?;
- for member in member_vec {
+ for member in vec {
let user_id = member.user.read().unwrap().id;
members.insert(user_id, member);
@@ -72,21 +49,24 @@ pub fn decode_guild_members(guild_id: GuildId, value: Value) -> Result<HashMap<U
Ok(members)
}
-pub fn decode_presences(value: Value) -> Result<HashMap<UserId, Presence>> {
+pub fn deserialize_presences<D: Deserializer>(deserializer: D)
+ -> StdResult<HashMap<UserId, Presence>, D::Error> {
+ let vec: Vec<Presence> = Deserialize::deserialize(deserializer)?;
let mut presences = HashMap::new();
- for presence in decode_array(value, Presence::decode)? {
+ for presence in vec {
presences.insert(presence.user_id, presence);
}
Ok(presences)
}
-pub fn decode_private_channels(value: Value)
- -> Result<HashMap<ChannelId, Channel>> {
+pub fn deserialize_private_channels<D: Deserializer>(deserializer: D)
+ -> StdResult<HashMap<ChannelId, Channel>, D::Error> {
+ let vec: Vec<Channel> = Deserialize::deserialize(deserializer)?;
let mut private_channels = HashMap::new();
- for private_channel in decode_array(value, Channel::decode)? {
+ for private_channel in vec {
let id = match private_channel {
Channel::Group(ref group) => group.read().unwrap().channel_id,
Channel::Private(ref channel) => channel.read().unwrap().id,
@@ -99,103 +79,54 @@ pub fn decode_private_channels(value: Value)
Ok(private_channels)
}
-pub fn decode_roles(value: Value) -> Result<HashMap<RoleId, Role>> {
+pub fn deserialize_roles<D: Deserializer>(deserializer: D)
+ -> StdResult<HashMap<RoleId, Role>, D::Error> {
+ let vec: Vec<Role> = Deserialize::deserialize(deserializer)?;
let mut roles = HashMap::new();
- for role in decode_array(value, Role::decode)? {
+ for role in vec {
roles.insert(role.id, role);
}
Ok(roles)
}
-pub fn decode_shards(value: Value) -> Result<[u64; 2]> {
- let array = into_array(value)?;
+pub fn deserialize_single_recipient<D: Deserializer>(deserializer: D)
+ -> StdResult<Arc<RwLock<User>>, D::Error> {
+ let mut users: Vec<User> = Deserialize::deserialize(deserializer)?;
+ let user = if users.is_empty() {
+ return Err(DeError::custom("Expected a single recipient"));
+ } else {
+ users.remove(0)
+ };
- Ok([
- req!(array.get(0)
- .ok_or(Error::Client(ClientError::InvalidShards))?.as_u64()) as u64,
- req!(array.get(1)
- .ok_or(Error::Client(ClientError::InvalidShards))?.as_u64()) as u64,
- ])
+ Ok(Arc::new(RwLock::new(user)))
}
-pub fn decode_users(value: Value) -> Result<HashMap<UserId, Arc<RwLock<User>>>> {
+pub fn deserialize_users<D: Deserializer>(deserializer: D)
+ -> StdResult<HashMap<UserId, Arc<RwLock<User>>>, D::Error> {
+ let vec: Vec<User> = Deserialize::deserialize(deserializer)?;
let mut users = HashMap::new();
- for user in decode_array(value, User::decode)? {
+ for user in vec {
users.insert(user.id, Arc::new(RwLock::new(user)));
}
Ok(users)
}
-pub fn decode_voice_states(value: Value)
- -> Result<HashMap<UserId, VoiceState>> {
+pub fn deserialize_voice_states<D: Deserializer>(deserializer: D)
+ -> StdResult<HashMap<UserId, VoiceState>, D::Error> {
+ let vec: Vec<VoiceState> = Deserialize::deserialize(deserializer)?;
let mut voice_states = HashMap::new();
- for voice_state in decode_array(value, VoiceState::decode)? {
+ for voice_state in vec {
voice_states.insert(voice_state.user_id, voice_state);
}
Ok(voice_states)
}
-pub fn into_string(value: Value) -> Result<String> {
- match value {
- Value::String(s) => Ok(s),
- Value::U64(v) => Ok(v.to_string()),
- Value::I64(v) => Ok(v.to_string()),
- value => Err(Error::Decode("Expected string", value)),
- }
-}
-
-pub fn into_map(value: Value) -> Result<BTreeMap<String, Value>> {
- match value {
- Value::Object(m) => Ok(m),
- value => Err(Error::Decode("Expected object", value)),
- }
-}
-
-pub fn into_u64(value: Value) -> Result<u64> {
- match value {
- Value::I64(v) => Ok(v as u64),
- Value::String(v) => match v.parse::<u64>() {
- Ok(v) => Ok(v),
- Err(_) => Err(Error::Decode("Expected valid u64", Value::String(v))),
- },
- Value::U64(v) => Ok(v),
- value => Err(Error::Decode("Expected u64", value)),
- }
-}
-
-pub fn opt<F, T>(map: &mut BTreeMap<String, Value>, key: &str, f: F)
- -> Result<Option<T>> where F: FnOnce(Value) -> Result<T> {
- match map.remove(key) {
- None | Some(Value::Null) => Ok(None),
- Some(val) => f(val).map(Some),
- }
-}
-
-pub fn decode_discriminator(value: Value) -> Result<u16> {
- match value {
- Value::I64(v) => Ok(v as u16),
- Value::U64(v) => Ok(v as u16),
- Value::String(s) => match s.parse::<u16>() {
- Ok(v) => Ok(v),
- Err(_) => Err(Error::Decode("Error parsing discriminator as u16",
- Value::String(s))),
- },
- value => Err(Error::Decode("Expected string or u64", value)),
- }
-}
-
-pub fn remove(map: &mut BTreeMap<String, Value>, key: &str) -> Result<Value> {
- map.remove(key).ok_or_else(|| {
- Error::Decode("Unexpected absent key", Value::String(key.into()))
- })
-}
-
#[cfg(feature="cache")]
pub fn user_has_perms(channel_id: ChannelId,
mut permissions: Permissions)
diff --git a/src/model/voice.rs b/src/model/voice.rs
new file mode 100644
index 0000000..10c74b2
--- /dev/null
+++ b/src/model/voice.rs
@@ -0,0 +1,36 @@
+use super::*;
+
+/// Information about an available voice region.
+#[derive(Clone, Debug, Deserialize)]
+pub struct VoiceRegion {
+ /// Whether it is a custom voice region, which is used for events.
+ pub custom: bool,
+ /// Whether it is a deprecated voice region, which you should avoid using.
+ pub deprecated: bool,
+ /// The internal Id of the voice region.
+ pub id: String,
+ /// A recognizable name of the location of the voice region.
+ pub name: String,
+ /// Whether the voice region is optimal for use by the current user.
+ pub optional: bool,
+ /// an example hostname.
+ pub sample_hostname: String,
+ /// An example port.
+ pub sample_port: u64,
+ /// Indicator of whether the voice region is only for VIP guilds.
+ pub vip: bool,
+}
+
+/// A user's state within a voice channel.
+#[derive(Clone, Debug, Deserialize)]
+pub struct VoiceState {
+ pub channel_id: Option<ChannelId>,
+ pub deaf: bool,
+ pub mute: bool,
+ pub self_deaf: bool,
+ pub self_mute: bool,
+ pub session_id: String,
+ pub suppress: bool,
+ pub token: Option<String>,
+ pub user_id: UserId,
+}
diff --git a/src/model/webhook.rs b/src/model/webhook.rs
index 4f831d9..0a2718e 100644
--- a/src/model/webhook.rs
+++ b/src/model/webhook.rs
@@ -1,10 +1,42 @@
-use serde_json::builder::ObjectBuilder;
use std::mem;
-use super::{Message, Webhook, WebhookId};
+use super::*;
use ::utils::builder::ExecuteWebhook;
use ::client::rest;
use ::internal::prelude::*;
+/// A representation of a webhook, which is a low-effort way to post messages to
+/// channels. They do not necessarily require a bot user or authentication to
+/// use.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Webhook {
+ /// The unique Id.
+ ///
+ /// Can be used to calculate the creation date of the webhook.
+ pub id: WebhookId,
+ /// The default avatar.
+ ///
+ /// This can be modified via [`ExecuteWebhook::avatar`].
+ ///
+ /// [`ExecuteWebhook::avatar`]: ../utils/builder/struct.ExecuteWebhook.html#method.avatar
+ pub avatar: Option<String>,
+ /// The Id of the channel that owns the webhook.
+ pub channel_id: ChannelId,
+ /// The Id of the guild that owns the webhook.
+ pub guild_id: Option<GuildId>,
+ /// The default name of the webhook.
+ ///
+ /// This can be modified via [`ExecuteWebhook::username`].
+ ///
+ /// [`ExecuteWebhook::username`]: ../utils/builder/struct.ExecuteWebhook.html#method.username
+ pub name: Option<String>,
+ /// The webhook's secure token.
+ pub token: String,
+ /// The user that created the webhook.
+ ///
+ /// **Note**: This is not received when getting a webhook by its token.
+ pub user: Option<User>,
+}
+
impl Webhook {
/// Deletes the webhook.
///
@@ -63,16 +95,15 @@ impl Webhook {
///
/// [`rest::edit_webhook`]: ../client/rest/fn.edit_webhook.html
/// [`rest::edit_webhook_with_token`]: ../client/rest/fn.edit_webhook_with_token.html
- pub fn edit(&mut self, name: Option<&str>, avatar: Option<&str>)
- -> Result<()> {
+ pub fn edit(&mut self, name: Option<&str>, avatar: Option<&str>) -> Result<()> {
if name.is_none() && avatar.is_none() {
return Ok(());
}
- let mut map = ObjectBuilder::new();
+ let mut map = Map::new();
if let Some(avatar) = avatar {
- map = map.insert("avatar", if avatar.is_empty() {
+ map.insert("avatar".to_owned(), if avatar.is_empty() {
Value::Null
} else {
Value::String(avatar.to_owned())
@@ -80,10 +111,10 @@ impl Webhook {
}
if let Some(name) = name {
- map = map.insert("name", name);
+ map.insert("name".to_owned(), Value::String(name.to_owned()));
}
- match rest::edit_webhook_with_token(self.id.0, &self.token, &map.build()) {
+ match rest::edit_webhook_with_token(self.id.0, &self.token, &map) {
Ok(replacement) => {
mem::replace(self, replacement);
@@ -142,7 +173,7 @@ impl Webhook {
/// ```
#[inline]
pub fn execute<F: FnOnce(ExecuteWebhook) -> ExecuteWebhook>(&self, f: F) -> Result<Message> {
- rest::execute_webhook(self.id.0, &self.token, &f(ExecuteWebhook::default()).0.build())
+ rest::execute_webhook(self.id.0, &self.token, &f(ExecuteWebhook::default()).0)
}
/// Retrieves the latest information about the webhook, editing the
diff --git a/src/utils/builder/create_embed.rs b/src/utils/builder/create_embed.rs
index 86d5b11..7c1c606 100644
--- a/src/utils/builder/create_embed.rs
+++ b/src/utils/builder/create_embed.rs
@@ -15,11 +15,10 @@
//! [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
//! [here]: https://discordapp.com/developers/docs/resources/channel#embed-object
-use serde_json::builder::ObjectBuilder;
use serde_json::Value;
-use std::collections::BTreeMap;
use std::default::Default;
use time::Tm;
+use ::internal::prelude::*;
use ::model::Embed;
use ::utils::Colour;
@@ -34,7 +33,7 @@ use ::utils::Colour;
/// [`Context::send_message`]: ../../client/struct.Context.html#method.send_message
/// [`Embed`]: ../../model/struct.Embed.html
/// [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
-pub struct CreateEmbed(pub BTreeMap<String, Value>);
+pub struct CreateEmbed(pub Map<String, Value>);
impl CreateEmbed {
/// Set the author of the embed.
@@ -45,9 +44,9 @@ impl CreateEmbed {
/// [`CreateEmbedAuthor`]: struct.CreateEmbedAuthor.html
pub fn author<F>(mut self, f: F) -> Self
where F: FnOnce(CreateEmbedAuthor) -> CreateEmbedAuthor {
- let author = f(CreateEmbedAuthor::default()).0.build();
+ let author = f(CreateEmbedAuthor::default()).0;
- self.0.insert("author".to_owned(), author);
+ self.0.insert("author".to_owned(), Value::Object(author));
CreateEmbed(self.0)
}
@@ -63,7 +62,7 @@ impl CreateEmbed {
/// Set the colour of the left-hand side of the embed.
pub fn colour<C: Into<Colour>>(mut self, colour: C) -> Self {
- self.0.insert("color".to_owned(), Value::U64(colour.into().0 as u64));
+ self.0.insert("color".to_owned(), Value::Number(Number::from(colour.into().0 as u64)));
CreateEmbed(self.0)
}
@@ -89,7 +88,7 @@ impl CreateEmbed {
/// [`CreateEmbedField`]: struct.CreateEmbedField.html
pub fn field<F>(mut self, f: F) -> Self
where F: FnOnce(CreateEmbedField) -> CreateEmbedField {
- let field = f(CreateEmbedField::default()).0.build();
+ let field = f(CreateEmbedField::default()).0;
{
let key = "fields".to_owned();
@@ -106,7 +105,7 @@ impl CreateEmbed {
return CreateEmbed(self.0);
},
};
- arr.push(field);
+ arr.push(Value::Object(field));
self.0.insert("fields".to_owned(), Value::Array(arr));
}
@@ -122,18 +121,18 @@ impl CreateEmbed {
/// [`CreateEmbedFooter`]: struct.CreateEmbedFooter.html
pub fn footer<F>(mut self, f: F) -> Self
where F: FnOnce(CreateEmbedFooter) -> CreateEmbedFooter {
- let footer = f(CreateEmbedFooter::default()).0.build();
+ let footer = f(CreateEmbedFooter::default()).0;
- self.0.insert("footer".to_owned(), footer);
+ self.0.insert("footer".to_owned(), Value::Object(footer));
CreateEmbed(self.0)
}
/// Set the image associated with the embed. This only supports HTTP(S).
pub fn image(mut self, url: &str) -> Self {
- let image = ObjectBuilder::new()
- .insert("url".to_owned(), url.to_owned())
- .build();
+ let image = json!({
+ "url": url.to_owned()
+ });
self.0.insert("image".to_owned(), image);
@@ -142,9 +141,9 @@ impl CreateEmbed {
/// Set the thumbnail of the embed. This only supports HTTP(S).
pub fn thumbnail(mut self, url: &str) -> Self {
- let thumbnail = ObjectBuilder::new()
- .insert("url".to_owned(), url.to_owned())
- .build();
+ let thumbnail = json!({
+ "url": url.to_owned(),
+ });
self.0.insert("thumbnail".to_owned(), thumbnail);
@@ -198,7 +197,7 @@ impl CreateEmbed {
impl Default for CreateEmbed {
/// Creates a builder with default values, setting the `type` to `rich`.
fn default() -> CreateEmbed {
- let mut map = BTreeMap::new();
+ let mut map = Map::new();
map.insert("type".to_owned(), Value::String("rich".to_owned()));
CreateEmbed(map)
@@ -233,13 +232,11 @@ impl From<Embed> for CreateEmbed {
b = b.description(&description);
}
- if let Some(fields) = embed.fields {
- for field in fields {
- b = b.field(move |f| f
- .inline(field.inline)
- .name(&field.name)
- .value(&field.value));
- }
+ for field in embed.fields {
+ b = b.field(move |f| f
+ .inline(field.inline)
+ .name(&field.name)
+ .value(&field.value));
}
if let Some(image) = embed.image {
@@ -266,7 +263,6 @@ impl From<Embed> for CreateEmbed {
}
}
-
/// A builder to create a fake [`Embed`] object's author, for use with the
/// [`CreateEmbed::author`] method.
///
@@ -276,22 +272,28 @@ impl From<Embed> for CreateEmbed {
/// [`CreateEmbed::author`]: struct.CreateEmbed.html#method.author
/// [`name`]: #method.name
#[derive(Default)]
-pub struct CreateEmbedAuthor(pub ObjectBuilder);
+pub struct CreateEmbedAuthor(pub Map<String, Value>);
impl CreateEmbedAuthor {
/// Set the URL of the author's icon.
- pub fn icon_url(self, icon_url: &str) -> Self {
- CreateEmbedAuthor(self.0.insert("icon_url", icon_url))
+ pub fn icon_url(mut self, icon_url: &str) -> Self {
+ self.0.insert("icon_url".to_owned(), Value::String(icon_url.to_owned()));
+
+ self
}
/// Set the author's name.
- pub fn name(self, name: &str) -> Self {
- CreateEmbedAuthor(self.0.insert("name", name))
+ pub fn name(mut self, name: &str) -> Self {
+ self.0.insert("name".to_owned(), Value::String(name.to_owned()));
+
+ self
}
/// Set the author's URL.
- pub fn url(self, url: &str) -> Self {
- CreateEmbedAuthor(self.0.insert("url", url))
+ pub fn url(mut self, url: &str) -> Self {
+ self.0.insert("url".to_owned(), Value::String(url.to_owned()));
+
+ self
}
}
@@ -303,22 +305,28 @@ impl CreateEmbedAuthor {
///
/// [`Embed`]: ../../model/struct.Embed.html
/// [`CreateEmbed::field`]: struct.CreateEmbed.html#method.field
-pub struct CreateEmbedField(pub ObjectBuilder);
+pub struct CreateEmbedField(pub Map<String, Value>);
impl CreateEmbedField {
/// Set whether the field is inlined. Set to true by default.
- pub fn inline(self, inline: bool) -> Self {
- CreateEmbedField(self.0.insert("inline", inline))
+ pub fn inline(mut self, inline: bool) -> Self {
+ self.0.insert("inline".to_owned(), Value::Bool(inline));
+
+ self
}
/// Set the field's name. It can't be longer than 256 characters.
- pub fn name(self, name: &str) -> Self {
- CreateEmbedField(self.0.insert("name", name))
+ pub fn name(mut self, name: &str) -> Self {
+ self.0.insert("name".to_owned(), Value::String(name.to_owned()));
+
+ self
}
/// Set the field's value. It can't be longer than 1024 characters.
- pub fn value(self, value: &str) -> Self {
- CreateEmbedField(self.0.insert("value", value))
+ pub fn value(mut self, value: &str) -> Self {
+ self.0.insert("value".to_owned(), Value::String(value.to_owned()));
+
+ self
}
}
@@ -326,7 +334,10 @@ impl Default for CreateEmbedField {
/// Creates a builder with default values, setting the value of `inline` to
/// `true`.
fn default() -> CreateEmbedField {
- CreateEmbedField(ObjectBuilder::new().insert("inline", true))
+ let mut map = Map::new();
+ map.insert("inline".to_owned(), Value::Bool(true));
+
+ CreateEmbedField(map)
}
}
@@ -338,17 +349,21 @@ impl Default for CreateEmbedField {
/// [`Embed`]: ../../model/struct.Embed.html
/// [`CreateEmbed::footer`]: struct.CreateEmbed.html#method.footer
#[derive(Default)]
-pub struct CreateEmbedFooter(pub ObjectBuilder);
+pub struct CreateEmbedFooter(pub Map<String, Value>);
impl CreateEmbedFooter {
/// Set the icon URL's value. This only supports HTTP(S).
- pub fn icon_url(self, icon_url: &str) -> Self {
- CreateEmbedFooter(self.0.insert("icon_url", icon_url))
+ pub fn icon_url(mut self, icon_url: &str) -> Self {
+ self.0.insert("icon_url".to_owned(), Value::String(icon_url.to_owned()));
+
+ self
}
/// Set the footer's text.
- pub fn text(self, text: &str) -> Self {
- CreateEmbedFooter(self.0.insert("text", text))
+ pub fn text(mut self, text: &str) -> Self {
+ self.0.insert("text".to_owned(), Value::String(text.to_owned()));
+
+ self
}
}
diff --git a/src/utils/builder/create_invite.rs b/src/utils/builder/create_invite.rs
index 99125b3..d7595cd 100644
--- a/src/utils/builder/create_invite.rs
+++ b/src/utils/builder/create_invite.rs
@@ -1,6 +1,6 @@
-use serde_json::builder::ObjectBuilder;
use serde_json::Value;
use std::default::Default;
+use ::internal::prelude::*;
/// A builder to create a [`RichInvite`] for use via [`Context::create_invite`].
///
@@ -28,7 +28,7 @@ use std::default::Default;
///
/// [`Context::create_invite`]: ../../client/struct.Context.html#method.create_invite
/// [`RichInvite`]: ../../model/struct.Invite.html
-pub struct CreateInvite(pub ObjectBuilder);
+pub struct CreateInvite(pub JsonMap);
impl CreateInvite {
/// The duration that the invite will be valid for.
@@ -36,8 +36,10 @@ impl CreateInvite {
/// Set to `0` for an invite which does not expire after an amount of time.
///
/// Defaults to `86400`, or 24 hours.
- pub fn max_age(self, max_age: u64) -> Self {
- CreateInvite(self.0.insert("max_age", max_age))
+ pub fn max_age(mut self, max_age: u64) -> Self {
+ self.0.insert("max_age".to_owned(), Value::Number(Number::from(max_age)));
+
+ self
}
/// The number of uses that the invite will be valid for.
@@ -45,28 +47,37 @@ impl CreateInvite {
/// Set to `0` for an invite which does not expire after a number of uses.
///
/// Defaults to `0`.
- pub fn max_uses(self, max_uses: u64) -> Self {
- CreateInvite(self.0.insert("max_uses", max_uses))
+ pub fn max_uses(mut self, max_uses: u64) -> Self {
+ self.0.insert("max_uses".to_owned(), Value::Number(Number::from(max_uses)));
+
+ self
}
/// Whether an invite grants a temporary membership.
///
/// Defaults to `false`.
- pub fn temporary(self, temporary: bool) -> Self {
- CreateInvite(self.0.insert("temporary", temporary))
+ pub fn temporary(mut self, temporary: bool) -> Self {
+ self.0.insert("temporary".to_owned(), Value::Bool(temporary));
+
+ self
}
/// Whether or not to try to reuse a similar invite.
///
/// Defaults to `false`.
- pub fn unique(self, unique: bool) -> Self {
- CreateInvite(self.0.insert("unique", unique))
+ pub fn unique(mut self, unique: bool) -> Self {
+ self.0.insert("unique".to_owned(), Value::Bool(unique));
+
+ self
}
}
impl Default for CreateInvite {
/// Creates a builder with default values, setting `validate` to `null`.
fn default() -> CreateInvite {
- CreateInvite(ObjectBuilder::new().insert("validate", Value::Null))
+ let mut map = Map::new();
+ map.insert("validate".to_owned(), Value::Null);
+
+ CreateInvite(map)
}
}
diff --git a/src/utils/builder/create_message.rs b/src/utils/builder/create_message.rs
index 8aed477..2dbbd79 100644
--- a/src/utils/builder/create_message.rs
+++ b/src/utils/builder/create_message.rs
@@ -1,7 +1,5 @@
-use serde_json::Value;
-use std::collections::BTreeMap;
-use std::default::Default;
use super::CreateEmbed;
+use ::internal::prelude::*;
/// A builder to specify the contents of an [`rest::send_message`] request,
/// primarily meant for use through [`Context::send_message`].
@@ -38,7 +36,7 @@ use super::CreateEmbed;
/// [`content`]: #method.content
/// [`embed`]: #method.embed
/// [`rest::send_message`]: ../../client/rest/fn.send_message.html
-pub struct CreateMessage(pub BTreeMap<String, Value>);
+pub struct CreateMessage(pub Map<String, Value>);
impl CreateMessage {
/// Set the content of the message.
@@ -79,7 +77,7 @@ impl Default for CreateMessage {
/// [`Message`]: ../../model/struct.Message.html
/// [`tts`]: #method.tts
fn default() -> CreateMessage {
- let mut map = BTreeMap::default();
+ let mut map = Map::default();
map.insert("tts".to_owned(), Value::Bool(false));
CreateMessage(map)
diff --git a/src/utils/builder/edit_channel.rs b/src/utils/builder/edit_channel.rs
index e136cbf..2fa61a5 100644
--- a/src/utils/builder/edit_channel.rs
+++ b/src/utils/builder/edit_channel.rs
@@ -1,5 +1,4 @@
-use serde_json::builder::ObjectBuilder;
-use std::default::Default;
+use ::internal::prelude::*;
/// A builder to edit a [`GuildChannel`] for use via one of a couple methods.
///
@@ -24,7 +23,8 @@ use std::default::Default;
/// [`Context::edit_channel`]: ../client/struct.Context.html#method.edit_channel
/// [`GuildChannel`]: ../model/struct.GuildChannel.html
/// [`GuildChannel::edit`]: ../model/struct.GuildChannel.html#method.edit
-pub struct EditChannel(pub ObjectBuilder);
+#[derive(Default)]
+pub struct EditChannel(pub JsonMap);
impl EditChannel {
/// The bitrate of the channel in bits.
@@ -32,20 +32,26 @@ impl EditChannel {
/// This is for [voice] channels only.
///
/// [voice]: ../../model/enum.ChannelType.html#variant.Voice
- pub fn bitrate(self, bitrate: u64) -> Self {
- EditChannel(self.0.insert("bitrate", bitrate))
+ pub fn bitrate(mut self, bitrate: u64) -> Self {
+ self.0.insert("bitrate".to_owned(), Value::Number(Number::from(bitrate)));
+
+ self
}
/// The name of the channel.
///
/// Must be between 2 and 100 characters long.
- pub fn name(self, name: &str) -> Self {
- EditChannel(self.0.insert("name", name))
+ pub fn name(mut self, name: &str) -> Self {
+ self.0.insert("name".to_owned(), Value::String(name.to_owned()));
+
+ self
}
/// The position of the channel in the channel list.
- pub fn position(self, position: u64) -> Self {
- EditChannel(self.0.insert("position", position))
+ pub fn position(mut self, position: u64) -> Self {
+ self.0.insert("position".to_owned(), Value::Number(Number::from(position)));
+
+ self
}
/// The topic of the channel. Can be empty.
@@ -55,8 +61,10 @@ impl EditChannel {
/// This is for [text] channels only.
///
/// [text]: ../../model/enum.ChannelType.html#variant.Text
- pub fn topic(self, topic: &str) -> Self {
- EditChannel(self.0.insert("topic", topic))
+ pub fn topic(mut self, topic: &str) -> Self {
+ self.0.insert("topic".to_owned(), Value::String(topic.to_owned()));
+
+ self
}
/// The number of users that may be in the channel simultaneously.
@@ -64,14 +72,9 @@ impl EditChannel {
/// This is for [voice] channels only.
///
/// [voice]: ../../model/enum.ChannelType.html#variant.Voice
- pub fn user_limit(self, user_limit: u64) -> Self {
- EditChannel(self.0.insert("user_limit", user_limit))
- }
-}
+ pub fn user_limit(mut self, user_limit: u64) -> Self {
+ self.0.insert("user_limit".to_owned(), Value::Number(Number::from(user_limit)));
-impl Default for EditChannel {
- /// Creates a builder with no default parameters.
- fn default() -> EditChannel {
- EditChannel(ObjectBuilder::new())
+ self
}
}
diff --git a/src/utils/builder/edit_guild.rs b/src/utils/builder/edit_guild.rs
index 83d5016..bd94efa 100644
--- a/src/utils/builder/edit_guild.rs
+++ b/src/utils/builder/edit_guild.rs
@@ -1,6 +1,4 @@
-use serde_json::builder::ObjectBuilder;
-use serde_json::Value;
-use std::default::Default;
+use ::internal::prelude::*;
use ::model::{ChannelId, Region, UserId, VerificationLevel};
/// A builder to optionally edit certain fields of a [`Guild`]. This is meant
@@ -13,7 +11,8 @@ use ::model::{ChannelId, Region, UserId, VerificationLevel};
/// [`Guild`]: ../../model/struct.Guild.html
/// [`LiveGuild::edit`]: ../../model/struct.LiveGuild.html#method.edit
/// [Manage Guild]: ../../model/permissions/constant.MANAGE_GUILD.html
-pub struct EditGuild(pub ObjectBuilder);
+#[derive(Default)]
+pub struct EditGuild(pub Map<String, Value>);
impl EditGuild {
/// Set the "AFK voice channel" that users are to move to if they have been
@@ -24,19 +23,23 @@ impl EditGuild {
/// valid.
///
/// [`afk_timeout`]: #method.afk_timeout
- pub fn afk_channel<C: Into<ChannelId>>(self, channel: Option<C>) -> Self {
- EditGuild(self.0.insert("afk_channel_id", match channel {
- Some(channel) => Value::U64(channel.into().0),
+ pub fn afk_channel<C: Into<ChannelId>>(mut self, channel: Option<C>) -> Self {
+ self.0.insert("afk_channel_id".to_owned(), match channel {
+ Some(channel) => Value::Number(Number::from(channel.into().0)),
None => Value::Null,
- }))
+ });
+
+ self
}
/// Set the amount of time a user is to be moved to the AFK channel -
/// configured via [`afk_channel`] - after being AFK.
///
/// [`afk_channel`]: #method.afk_channel
- pub fn afk_timeout(self, timeout: u64) -> Self {
- EditGuild(self.0.insert("afk_timeout", timeout))
+ pub fn afk_timeout(mut self, timeout: u64) -> Self {
+ self.0.insert("afk_timeout".to_owned(), Value::Number(Number::from(timeout)));
+
+ self
}
/// Set the icon of the guild. Pass `None` to remove the icon.
@@ -58,25 +61,28 @@ impl EditGuild {
/// ```
///
/// [`utils::read_image`]: ../../utils/fn.read_image.html
- pub fn icon(self, icon: Option<&str>) -> Self {
- EditGuild(self.0
- .insert("icon",
- icon.map_or_else(|| Value::Null,
- |x| Value::String(x.to_owned()))))
+ pub fn icon(mut self, icon: Option<&str>) -> Self {
+ self.0.insert("icon".to_owned(), icon.map_or_else(|| Value::Null, |x| Value::String(x.to_owned())));
+
+ self
}
/// Set the name of the guild.
///
/// **Note**: Must be between (and including) 2-100 chracters.
- pub fn name(self, name: &str) -> Self {
- EditGuild(self.0.insert("name", name))
+ pub fn name(mut self, name: &str) -> Self {
+ self.0.insert("name".to_owned(), Value::String(name.to_owned()));
+
+ self
}
/// Transfers the ownership of the guild to another user by Id.
///
/// **Note**: The current user must be the owner of the guild.
- pub fn owner<U: Into<UserId>>(self, user_id: U) -> Self {
- EditGuild(self.0.insert("owner_id", user_id.into().0))
+ pub fn owner<U: Into<UserId>>(mut self, user_id: U) -> Self {
+ self.0.insert("owner_id".to_owned(), Value::Number(Number::from(user_id.into().0)));
+
+ self
}
/// Set the voice region of the server.
@@ -96,8 +102,10 @@ impl EditGuild {
/// ```
///
/// [`Region::UsWest`]: ../../model/enum.Region.html#variant.UsWest
- pub fn region(self, region: Region) -> Self {
- EditGuild(self.0.insert("region", region.name()))
+ pub fn region(mut self, region: Region) -> Self {
+ self.0.insert("region".to_owned(), Value::String(region.name().to_owned()));
+
+ self
}
/// Set the splash image of the guild on the invitation page.
@@ -107,11 +115,12 @@ impl EditGuild {
///
/// [`InviteSplash`]: ../../model/enum.Feature.html#variant.InviteSplash
/// [`features`]: ../../model/struct.LiveGuild.html#structfield.features
- pub fn splash(self, splash: Option<&str>) -> Self {
- EditGuild(self.0
- .insert("splash",
- splash.map_or_else(|| Value::Null,
- |x| Value::String(x.to_owned()))))
+ pub fn splash(mut self, splash: Option<&str>) -> Self {
+ let splash = splash.map_or(Value::Null, |x| Value::String(x.to_owned()));
+
+ self.0.insert("splash".to_owned(), splash);
+
+ self
}
/// Set the verification level of the guild. This can restrict what a
@@ -145,15 +154,12 @@ impl EditGuild {
///
/// [`VerificationLevel`]: ../../model/enum.VerificationLevel.html
/// [`VerificationLevel::High`]: ../../model/enum.VerificationLevel.html#variant.High
- pub fn verification_level<V>(self, verification_level: V) -> Self
+ pub fn verification_level<V>(mut self, verification_level: V) -> Self
where V: Into<VerificationLevel> {
- EditGuild(self.0.insert("verification_level",
- verification_level.into().num()))
- }
-}
+ let num = Value::Number(Number::from(verification_level.into().num()));
+
+ self.0.insert("verification_level".to_owned(), num);
-impl Default for EditGuild {
- fn default() -> EditGuild {
- EditGuild(ObjectBuilder::new())
+ self
}
}
diff --git a/src/utils/builder/edit_member.rs b/src/utils/builder/edit_member.rs
index cb67214..58f260a 100644
--- a/src/utils/builder/edit_member.rs
+++ b/src/utils/builder/edit_member.rs
@@ -1,6 +1,5 @@
-use serde_json::builder::ObjectBuilder;
-use std::default::Default;
use ::model::{ChannelId, RoleId};
+use ::internal::prelude::*;
/// A builder which edits the properties of a [`Member`], to be used in
/// conjunction with [`Context::edit_member`] and [`Member::edit`].
@@ -8,7 +7,8 @@ use ::model::{ChannelId, RoleId};
/// [`Context::edit_member`]: ../../client/struct.Context.html#method.edit_member
/// [`Member`]: ../../model/struct.Member.html
/// [`Member::edit`]: ../../model/struct.Member.html#method.edit
-pub struct EditMember(pub ObjectBuilder);
+#[derive(Default)]
+pub struct EditMember(pub JsonMap);
impl EditMember {
/// Whether to deafen the member.
@@ -16,8 +16,10 @@ impl EditMember {
/// Requires the [Deafen Members] permission.
///
/// [Deafen Members]: ../../model/permissions/constant.DEAFEN_MEMBERS.html
- pub fn deafen(self, deafen: bool) -> Self {
- EditMember(self.0.insert("deaf", deafen))
+ pub fn deafen(mut self, deafen: bool) -> Self {
+ self.0.insert("deaf".to_owned(), Value::Bool(deafen));
+
+ self
}
/// Whether to mute the member.
@@ -25,8 +27,10 @@ impl EditMember {
/// Requires the [Mute Members] permission.
///
/// [Mute Members]: ../../model/permissions/constant.MUTE_MEMBERS.html
- pub fn mute(self, mute: bool) -> Self {
- EditMember(self.0.insert("mute", mute))
+ pub fn mute(mut self, mute: bool) -> Self {
+ self.0.insert("mute".to_owned(), Value::Bool(mute));
+
+ self
}
/// Changes the member's nickname. Pass an empty string to reset the
@@ -35,8 +39,10 @@ impl EditMember {
/// Requires the [Manage Nicknames] permission.
///
/// [Manage Nicknames]: ../../model/permissions/constant.MANAGE_NICKNAMES.html
- pub fn nickname(self, nickname: &str) -> Self {
- EditMember(self.0.insert("nick", nickname))
+ pub fn nickname(mut self, nickname: &str) -> Self {
+ self.0.insert("nick".to_owned(), Value::String(nickname.to_owned()));
+
+ self
}
/// Set the list of roles that the member should have.
@@ -44,10 +50,12 @@ impl EditMember {
/// Requires the [Manage Roles] permission to modify.
///
/// [Manage Roles]: ../../model/permissions/constant.MANAGE_ROLES.html
- pub fn roles(self, roles: &[RoleId]) -> Self {
- EditMember(self.0
- .insert_array("roles",
- |a| roles.iter().fold(a, |a, id| a.push(id.0))))
+ pub fn roles(mut self, roles: &[RoleId]) -> Self {
+ let role_ids = roles.iter().map(|x| Value::Number(Number::from(x.0))).collect();
+
+ self.0.insert("roles".to_owned(), Value::Array(role_ids));
+
+ self
}
/// The Id of the voice channel to move the member to.
@@ -55,13 +63,9 @@ impl EditMember {
/// Requires the [Move Members] permission.
///
/// [Move Members]: ../../model/permissions/constant.MOVE_MEMBERS.html
- pub fn voice_channel<C: Into<ChannelId>>(self, channel_id: C) -> Self {
- EditMember(self.0.insert("channel_id", channel_id.into().0))
- }
-}
+ pub fn voice_channel<C: Into<ChannelId>>(mut self, channel_id: C) -> Self {
+ self.0.insert("channel_id".to_owned(), Value::Number(Number::from(channel_id.into().0)));
-impl Default for EditMember {
- fn default() -> EditMember {
- EditMember(ObjectBuilder::new())
+ self
}
}
diff --git a/src/utils/builder/edit_profile.rs b/src/utils/builder/edit_profile.rs
index 83f1da2..680a371 100644
--- a/src/utils/builder/edit_profile.rs
+++ b/src/utils/builder/edit_profile.rs
@@ -1,12 +1,11 @@
-use serde_json::builder::ObjectBuilder;
-use serde_json::Value;
-use std::default::Default;
+use ::internal::prelude::*;
/// A builder to edit the current user's settings, to be used in conjunction
/// with [`Context::edit_profile`].
///
/// [`Context::edit_profile`]: ../../client/struct.Context.html#method.edit_profile
-pub struct EditProfile(pub ObjectBuilder);
+#[derive(Default)]
+pub struct EditProfile(pub JsonMap);
impl EditProfile {
/// Sets the avatar of the current user. `None` can be passed to remove an
@@ -33,11 +32,12 @@ impl EditProfile {
/// ```
///
/// [`utils::read_image`]: ../fn.read_image.html
- pub fn avatar(self, icon: Option<&str>) -> Self {
- EditProfile(self.0
- .insert("avatar",
- icon.map_or_else(|| Value::Null,
- |x| Value::String(x.to_owned()))))
+ pub fn avatar(mut self, avatar: Option<&str>) -> Self {
+ let avatar = avatar.map_or(Value::Null, |x| Value::String(x.to_owned()));
+
+ self.0.insert("avatar".to_owned(), avatar);
+
+ self
}
/// Modifies the current user's email address.
@@ -50,8 +50,10 @@ impl EditProfile {
/// **Note**: This can only be used by user accounts.
///
/// [provided]: #method.password
- pub fn email(self, email: &str) -> Self {
- EditProfile(self.0.insert("email", email))
+ pub fn email(mut self, email: &str) -> Self {
+ self.0.insert("email".to_owned(), Value::String(email.to_owned()));
+
+ self
}
/// Modifies the current user's password.
@@ -60,8 +62,10 @@ impl EditProfile {
/// [provided].
///
/// [provided]: #method.password
- pub fn new_password(self, new_password: &str) -> Self {
- EditProfile(self.0.insert("new_password", new_password))
+ pub fn new_password(mut self, new_password: &str) -> Self {
+ self.0.insert("new_password".to_owned(), Value::String(new_password.to_owned()));
+
+ self
}
/// Used for providing the current password as verification when
@@ -69,8 +73,10 @@ impl EditProfile {
///
/// [modifying the password]: #method.new_password
/// [modifying the associated email address]: #method.email
- pub fn password(self, password: &str) -> Self {
- EditProfile(self.0.insert("password", password))
+ pub fn password(mut self, password: &str) -> Self {
+ self.0.insert("password".to_owned(), Value::String(password.to_owned()));
+
+ self
}
/// Modifies the current user's username.
@@ -79,13 +85,9 @@ impl EditProfile {
/// and current discriminator, a new unique discriminator will be assigned.
/// If there are no available discriminators with the requested username,
/// an error will occur.
- pub fn username(self, username: &str) -> Self {
- EditProfile(self.0.insert("username", username))
- }
-}
+ pub fn username(mut self, username: &str) -> Self {
+ self.0.insert("username".to_owned(), Value::String(username.to_owned()));
-impl Default for EditProfile {
- fn default() -> EditProfile {
- EditProfile(ObjectBuilder::new())
+ self
}
}
diff --git a/src/utils/builder/edit_role.rs b/src/utils/builder/edit_role.rs
index e9b86e9..de8efc7 100644
--- a/src/utils/builder/edit_role.rs
+++ b/src/utils/builder/edit_role.rs
@@ -1,5 +1,5 @@
-use serde_json::builder::ObjectBuilder;
use std::default::Default;
+use ::internal::prelude::*;
use ::model::{Permissions, Role, permissions};
/// A builer to create or edit a [`Role`] for use via a number of model and
@@ -31,53 +31,67 @@ use ::model::{Permissions, Role, permissions};
/// [`Guild::create_role`]: ../../model/struct.Guild.html#method.create_role
/// [`Role`]: ../../model/struct.Role.html
/// [`Role::edit`]: ../../model/struct.Role.html#method.edit
-pub struct EditRole(pub ObjectBuilder);
+pub struct EditRole(pub JsonMap);
impl EditRole {
/// Creates a new builder with the values of the given [`Role`].
///
/// [`Role`]: ../../model/struct.Role.html
pub fn new(role: &Role) -> Self {
- EditRole(ObjectBuilder::new()
- .insert("color", role.colour.0)
- .insert("hoist", role.hoist)
- .insert("managed", role.managed)
- .insert("mentionable", role.mentionable)
- .insert("name", &role.name)
- .insert("permissions", role.permissions.bits())
- .insert("position", role.position))
+ let mut map = Map::new();
+ map.insert("color".to_owned(), Value::Number(Number::from(role.colour.0)));
+ map.insert("hoist".to_owned(), Value::Bool(role.hoist));
+ map.insert("managed".to_owned(), Value::Bool(role.managed));
+ map.insert("mentionable".to_owned(), Value::Bool(role.mentionable));
+ map.insert("name".to_owned(), Value::String(role.name.clone()));
+ map.insert("permissions".to_owned(), Value::Number(Number::from(role.permissions.bits())));
+ map.insert("position".to_owned(), Value::Number(Number::from(role.position)));
+
+ EditRole(map)
}
/// Sets the colour of the role.
- pub fn colour(self, colour: u64) -> Self {
- EditRole(self.0.insert("color", colour))
+ pub fn colour(mut self, colour: u64) -> Self {
+ self.0.insert("color".to_owned(), Value::Number(Number::from(colour)));
+
+ self
}
/// Whether or not to hoist the role above lower-positioned role in the user
/// list.
- pub fn hoist(self, hoist: bool) -> Self {
- EditRole(self.0.insert("hoist", hoist))
+ pub fn hoist(mut self, hoist: bool) -> Self {
+ self.0.insert("hoist".to_owned(), Value::Bool(hoist));
+
+ self
}
/// Whether or not to make the role mentionable, notifying its users.
- pub fn mentionable(self, mentionable: bool) -> Self {
- EditRole(self.0.insert("mentionable", mentionable))
+ pub fn mentionable(mut self, mentionable: bool) -> Self {
+ self.0.insert("mentionable".to_owned(), Value::Bool(mentionable));
+
+ self
}
/// The name of the role to set.
- pub fn name(self, name: &str) -> Self {
- EditRole(self.0.insert("name", name))
+ pub fn name(mut self, name: &str) -> Self {
+ self.0.insert("name".to_owned(), Value::String(name.to_owned()));
+
+ self
}
/// The set of permissions to assign the role.
- pub fn permissions(self, permissions: Permissions) -> Self {
- EditRole(self.0.insert("permissions", permissions.bits()))
+ pub fn permissions(mut self, permissions: Permissions) -> Self {
+ self.0.insert("permissions".to_owned(), Value::Number(Number::from(permissions.bits())));
+
+ self
}
/// The position to assign the role in the role list. This correlates to the
/// role's position in the user list.
- pub fn position(self, position: u8) -> Self {
- EditRole(self.0.insert("position", position))
+ pub fn position(mut self, position: u8) -> Self {
+ self.0.insert("position".to_owned(), Value::Number(Number::from(position)));
+
+ self
}
}
@@ -95,12 +109,16 @@ impl Default for EditRole {
///
/// [general permissions set]: ../../model/permissions/fn.general.html
fn default() -> EditRole {
- EditRole(ObjectBuilder::new()
- .insert("color", 10070709)
- .insert("hoist", false)
- .insert("mentionable", false)
- .insert("name", "new role".to_owned())
- .insert("permissions", permissions::general().bits())
- .insert("position", 1))
+ let mut map = Map::new();
+ let permissions = Number::from(permissions::general().bits());
+
+ map.insert("color".to_owned(), Value::Number(Number::from(10070709)));
+ map.insert("hoist".to_owned(), Value::Bool(false));
+ map.insert("mentionable".to_owned(), Value::Bool(false));
+ map.insert("name".to_owned(), Value::String("new role".to_owned()));
+ map.insert("permissions".to_owned(), Value::Number(permissions));
+ map.insert("position".to_owned(), Value::Number(Number::from(1)));
+
+ EditRole(map)
}
}
diff --git a/src/utils/builder/execute_webhook.rs b/src/utils/builder/execute_webhook.rs
index 073dcb3..5e79f31 100644
--- a/src/utils/builder/execute_webhook.rs
+++ b/src/utils/builder/execute_webhook.rs
@@ -1,6 +1,6 @@
-use serde_json::builder::ObjectBuilder;
use serde_json::Value;
use std::default::Default;
+use ::internal::prelude::*;
/// A builder to create the inner content of a [`Webhook`]'s execution.
///
@@ -52,12 +52,14 @@ use std::default::Default;
/// [`Webhook`]: ../../model/struct.Webhook.html
/// [`Webhook::execute`]: ../../model/struct.Webhook.html#method.execute
/// [`execute_webhook`]: ../../client/rest/fn.execute_webhook.html
-pub struct ExecuteWebhook(pub ObjectBuilder);
+pub struct ExecuteWebhook(pub JsonMap);
impl ExecuteWebhook {
/// Override the default avatar of the webhook with an image URL.
- pub fn avatar_url(self, avatar_url: &str) -> Self {
- ExecuteWebhook(self.0.insert("avatar_url", avatar_url))
+ pub fn avatar_url(mut self, avatar_url: &str) -> Self {
+ self.0.insert("avatar_url".to_owned(), Value::String(avatar_url.to_owned()));
+
+ self
}
/// Set the content of the message.
@@ -66,8 +68,10 @@ impl ExecuteWebhook {
/// omitted.
///
/// [`embeds`]: #method.embeds
- pub fn content(self, content: &str) -> Self {
- ExecuteWebhook(self.0.insert("content", content))
+ pub fn content(mut self, content: &str) -> Self {
+ self.0.insert("content".to_owned(), Value::String(content.to_owned()));
+
+ self
}
/// Set the embeds associated with the message.
@@ -83,20 +87,26 @@ impl ExecuteWebhook {
/// [`Embed::fake`]: ../../model/struct.Embed.html#method.fake
/// [`Webhook::execute`]: ../../model/struct.Webhook.html#method.execute
/// [struct-level documentation]: #examples
- pub fn embeds(self, embeds: Vec<Value>) -> Self {
- ExecuteWebhook(self.0.insert("embeds", embeds))
+ pub fn embeds(mut self, embeds: Vec<Value>) -> Self {
+ self.0.insert("embeds".to_owned(), Value::Array(embeds));
+
+ self
}
/// Whether the message is a text-to-speech message.
///
/// Think carefully before setting this to `true`.
- pub fn tts(self, tts: bool) -> Self {
- ExecuteWebhook(self.0.insert("tts", tts))
+ pub fn tts(mut self, tts: bool) -> Self {
+ self.0.insert("tts".to_owned(), Value::Bool(tts));
+
+ self
}
/// Override the default username of the webhook.
- pub fn username(self, username: &str) -> Self {
- ExecuteWebhook(self.0.insert("username", username))
+ pub fn username(mut self, username: &str) -> Self {
+ self.0.insert("username".to_owned(), Value::String(username.to_owned()));
+
+ self
}
}
@@ -110,6 +120,9 @@ impl Default for ExecuteWebhook {
/// [`Webhook`]: ../../model/struct.Webhook.html
/// [`tts`]: #method.tts
fn default() -> ExecuteWebhook {
- ExecuteWebhook(ObjectBuilder::new().insert("tts", false))
+ let mut map = Map::new();
+ map.insert("tts".to_owned(), Value::Bool(false));
+
+ ExecuteWebhook(map)
}
}
diff --git a/src/utils/colour.rs b/src/utils/colour.rs
index 69f549d..8687db0 100644
--- a/src/utils/colour.rs
+++ b/src/utils/colour.rs
@@ -1,6 +1,3 @@
-use std::default::Default;
-use ::internal::prelude::*;
-
macro_rules! colour {
($(#[$attr:meta] $name:ident, $val:expr;)*) => {
impl Colour {
@@ -64,7 +61,7 @@ macro_rules! colour {
/// [`Role`]: ../model/struct.Role.html
/// [`dark_teal`]: #method.dark_teal
/// [`get_g`]: #method.get_g
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Colour(pub u32);
impl Colour {
@@ -123,15 +120,6 @@ impl Colour {
Colour(uint)
}
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Colour> {
- match value {
- Value::U64(v) => Ok(Colour(v as u32)),
- Value::I64(v) => Ok(Colour(v as u32)),
- other => Err(Error::Decode("Expected valid colour", other)),
- }
- }
-
/// Returns the red RGB component of this Colour.
///
/// # Examples
diff --git a/src/utils/macros.rs b/src/utils/macros.rs
index 16382b1..be94046 100644
--- a/src/utils/macros.rs
+++ b/src/utils/macros.rs
@@ -114,3 +114,58 @@ macro_rules! feature_voice {
}
}
}
+
+#[macro_export]
+macro_rules! enum_number {
+ (#[$attr_:meta] $name:ident { $(#[$attr:meta] $variant:ident = $value:expr, )* }) => {
+ #[$attr_]
+ #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
+ pub enum $name {
+ $(
+ #[$attr]
+ $variant = $value,
+ )*
+ }
+
+ impl ::serde::Serialize for $name {
+ fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
+ where S: ::serde::Serializer
+ {
+ // Serialize the enum as a u64.
+ serializer.serialize_u64(*self as u64)
+ }
+ }
+
+ impl ::serde::Deserialize for $name {
+ fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
+ where D: ::serde::Deserializer
+ {
+ struct Visitor;
+
+ impl ::serde::de::Visitor for Visitor {
+ type Value = $name;
+
+ fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ formatter.write_str("positive integer")
+ }
+
+ fn visit_u64<E>(self, value: u64) -> StdResult<$name, E>
+ where E: ::serde::de::Error
+ {
+ // Rust does not come with a simple way of converting a
+ // number to an enum, so use a big `match`.
+ match value {
+ $( $value => Ok($name::$variant), )*
+ _ => Err(E::custom(
+ format!("unknown {} value: {}",
+ stringify!($name), value))),
+ }
+ }
+ }
+
+ // Deserialize the enum from a u64.
+ deserializer.deserialize_u64(Visitor)
+ }
+ }
+ }
+}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index ca20b57..235709e 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -22,19 +22,6 @@ use ::model::{EmojiIdentifier, EmojiId};
pub use self::message_builder::MessageBuilder;
-#[doc(hidden)]
-pub fn decode_array<T, F: Fn(Value) -> Result<T>>(value: Value, f: F) -> Result<Vec<T>> {
- into_array(value).and_then(|x| x.into_iter().map(f).collect())
-}
-
-#[doc(hidden)]
-pub fn into_array(value: Value) -> Result<Vec<Value>> {
- match value {
- Value::Array(v) => Ok(v),
- value => Err(Error::Decode("Expected array", value)),
- }
-}
-
/// Retrieves the "code" part of an [invite][`RichInvite`] out of a URL.
///
/// # Examples