aboutsummaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
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