aboutsummaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorZeyla Hellyer <[email protected]>2017-04-11 08:15:37 -0700
committerZeyla Hellyer <[email protected]>2017-04-11 10:52:43 -0700
commitf6b27eb39c042e6779edc2d5d4b6e6c27d133eaf (patch)
treea6169fee3bf9ea75391101577dcb2982e3daa388 /src/model
parentClippy lints + permission byte literals (diff)
downloadserenity-f6b27eb39c042e6779edc2d5d4b6e6c27d133eaf.tar.xz
serenity-f6b27eb39c042e6779edc2d5d4b6e6c27d133eaf.zip
Switch to using serde for deserialization
The current build system is rudimentary, incomplete, and rigid, offering little in the way of customizing decoding options. To solve this, switch to using serde-derive with custom Deserialization implementations. This allows very simple deserialization when special logic does not need to be applied, yet allows us to implement our own deserialization logic when required. The problem with the build system was that it built enums and structs from YAML files. This is not so good, because it requires creating a custom build system (which was rudimentary), creating "special struct configs" when logic needed to be ever so slightly extended (rigid), and if special logic needed to be applied, a custom deserialization method would have been needed to be made anyway (incomplete). To solve this, switch to serde-derive and implementing Deserialize ourselves where required. This reduces YAML definitions that might look like: ```yaml --- name: Group description: > A group channel, potentially including other users, separate from a [`Guild`]. [`Guild`]: struct.Guild.html fields: - name: channel_id description: The Id of the group channel. from: id type: ChannelId - name: icon description: The optional icon of the group channel. optional: true type: string - name: last_message_id description: The Id of the last message sent. optional: true type: MessageId - name: last_pin_timestamp description: Timestamp of the latest pinned message. optional: true type: string - name: name description: The name of the group channel. optional: true type: string - name: owner_id description: The Id of the group channel creator. type: UserId - name: recipients description: Group channel's members. custom: decode_users t: UserId, Arc<RwLock<User>> type: hashmap ``` to: ```rs /// A group channel - potentially including other [`User`]s - separate from a /// [`Guild`]. /// /// [`Guild`]: struct.Guild.html /// [`User`]: struct.User.html 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>>>, } ``` This is much simpler and does not have as much boilerplate. There should not be any backwards incompatible changes other than the old, public - yet undocumented (and hidden from documentation) - decode methods being removed. Due to the nature of this commit, field names may be incorrect, and will need to be corrected as deserialization errors are found.
Diffstat (limited to 'src/model')
-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
27 files changed, 2232 insertions, 1039 deletions
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