Add partial emoji and custom reaction types, refine SQLx mapping

This commit is contained in:
Quat3rnion 2024-06-17 15:22:58 -04:00
parent 590a6d6828
commit bec0269e70
10 changed files with 122 additions and 48 deletions

View File

@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize};
use serde_aux::prelude::deserialize_number_from_string;
use crate::types::{config::types::subconfigs::register::{
DateOfBirthConfiguration, PasswordConfiguration, RegistrationEmailConfiguration,
@ -22,6 +23,7 @@ pub struct RegisterConfiguration {
pub allow_multiple_accounts: bool,
pub block_proxies: bool,
pub incrementing_discriminators: bool,
#[serde(deserialize_with = "deserialize_number_from_string")]
pub default_rights: Rights,
}

View File

@ -64,7 +64,9 @@ pub struct Channel {
pub managed: Option<bool>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member: Option<ThreadMember>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub message_count: Option<i32>,
pub name: Option<String>,
pub nsfw: Option<bool>,
@ -75,6 +77,7 @@ pub struct Channel {
#[cfg(not(feature = "sqlx"))]
#[cfg_attr(feature = "client", observe_option_vec)]
pub permission_overwrites: Option<Vec<Shared<PermissionOverwrite>>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub permissions: Option<String>,
pub position: Option<i32>,
pub rate_limit_per_user: Option<i32>,
@ -85,6 +88,7 @@ pub struct Channel {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread_metadata: Option<ThreadMetadata>,
pub topic: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub total_message_sent: Option<i32>,
pub user_limit: Option<i32>,
pub video_quality_mode: Option<i32>,

View File

@ -6,7 +6,7 @@ use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use crate::types::Shared;
use crate::types::{PartialEmoji, Shared};
use crate::types::entities::User;
use crate::types::Snowflake;
@ -66,3 +66,18 @@ impl PartialEq for Emoji {
|| self.available != other.available)
}
}
impl From<PartialEmoji> for Emoji {
fn from(value: PartialEmoji) -> Self {
Self {
id: value.id.unwrap_or_default(), // TODO: this should be handled differently
name: Some(value.name),
roles: None,
user: None,
require_colons: Some(value.animated),
managed: None,
animated: Some(value.animated),
available: None,
}
}
}

View File

@ -59,7 +59,7 @@ pub struct Guild {
pub emojis: Vec<Shared<Emoji>>,
pub explicit_content_filter: Option<ExplicitContentFilterLevel>,
//#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))]
pub features: Option<GuildFeaturesList>,
pub features: GuildFeaturesList,
pub icon: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub icon_hash: Option<String>,
@ -111,7 +111,7 @@ pub struct Guild {
#[cfg_attr(feature = "client", observe_option_vec)]
pub webhooks: Option<Vec<Shared<Webhook>>>,
#[cfg(feature = "sqlx")]
pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>,
pub welcome_screen: sqlx::types::Json<Option<WelcomeScreenObject>>,
#[cfg(not(feature = "sqlx"))]
pub welcome_screen: Option<WelcomeScreenObject>,
pub widget_channel_id: Option<Snowflake>,

View File

@ -16,7 +16,9 @@ use super::{Application, Channel, GuildMember, NSFWLevel, User};
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Invite {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub approximate_member_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub approximate_presence_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub channel: Option<Channel>,
@ -45,7 +47,7 @@ pub struct Invite {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub target_user: Option<User>,
pub temporary: Option<bool>,
pub uses: Option<i32>,
pub uses: Option<u32>,
}
/// The guild an invite is for.
@ -77,16 +79,16 @@ impl From<Guild> for InviteGuild {
icon: value.icon,
splash: value.splash,
verification_level: value.verification_level.unwrap_or_default(),
features: value.features.unwrap_or_default(),
features: value.features,
vanity_url_code: value.vanity_url_code,
description: value.description,
banner: value.banner,
premium_subscription_count: value.premium_subscription_count,
nsfw_deprecated: None,
nsfw_level: value.nsfw_level.unwrap_or_default(),
welcome_screen: value.welcome_screen.map(|obj| {
welcome_screen: value.welcome_screen.0.map(|obj| {
#[cfg(feature = "sqlx")]
let res = obj.0;
let res = obj;
#[cfg(not(feature = "sqlx"))]
let res = obj;
res

View File

@ -75,13 +75,14 @@ pub struct Message {
pub interaction: Option<MessageInteraction>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread: Option<Channel>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg(feature = "sqlx")]
pub components: Option<sqlx::types::Json<Vec<Component>>>,
#[cfg(not(feature = "sqlx"))]
pub components: Option<Vec<Component>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub sticker_items: Option<Vec<StickerItem>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub stickers: Option<Vec<Sticker>>,
pub position: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub role_subscription_data: Option<RoleSubscriptionData>,
}
@ -116,7 +117,7 @@ impl PartialEq for Message {
&& self.thread == other.thread
&& self.components == other.components
&& self.sticker_items == other.sticker_items
&& self.position == other.position
// && self.position == other.position
&& self.role_subscription_data == other.role_subscription_data
}
}
@ -125,12 +126,22 @@ impl PartialEq for Message {
/// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/message#message-reference-object>
pub struct MessageReference {
#[serde(rename = "type")]
pub reference_type: MessageReferenceType,
pub message_id: Snowflake,
pub channel_id: Snowflake,
pub guild_id: Option<Snowflake>,
pub fail_if_not_exists: Option<bool>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Ord, PartialOrd)]
pub enum MessageReferenceType {
/// A standard reference used by replies and system messages
Default = 0,
/// A reference used to point to a message at a point in time
Forward = 1,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MessageInteraction {
pub id: Snowflake,
@ -405,3 +416,21 @@ bitflags! {
const HAS_SNAPSHOT = 1 << 14;
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct PartialEmoji {
#[serde(default)]
pub id: Option<Snowflake>,
pub name: String,
#[serde(default)]
pub animated: bool
}
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, PartialOrd)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[repr(u8)]
pub enum ReactionType {
Normal = 0,
Burst = 1, // The dreaded super reactions
}

View File

@ -59,7 +59,7 @@ pub struct GetChannelMessagesSchema {
/// Between 1 and 100, defaults to 50.
pub limit: Option<i32>,
#[serde(flatten)]
pub anchor: ChannelMessagesAnchor,
pub anchor: Option<ChannelMessagesAnchor>,
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
@ -74,21 +74,21 @@ impl GetChannelMessagesSchema {
pub fn before(anchor: Snowflake) -> Self {
Self {
limit: None,
anchor: ChannelMessagesAnchor::Before(anchor),
anchor: Some(ChannelMessagesAnchor::Before(anchor)),
}
}
pub fn around(anchor: Snowflake) -> Self {
Self {
limit: None,
anchor: ChannelMessagesAnchor::Around(anchor),
anchor: Some(ChannelMessagesAnchor::Around(anchor)),
}
}
pub fn after(anchor: Snowflake) -> Self {
Self {
limit: None,
anchor: ChannelMessagesAnchor::After(anchor),
anchor: Some(ChannelMessagesAnchor::After(anchor)),
}
}

View File

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::{
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
};
use crate::types::{Attachment, MessageFlags, MessageType, Snowflake};
use crate::types::{Attachment, MessageFlags, MessageType, ReactionType, Snowflake};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
@ -118,13 +118,21 @@ pub struct MessageAck {
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
pub struct MessageModifySchema {
content: Option<String>,
embeds: Option<Vec<Embed>>,
embed: Option<Embed>,
allowed_mentions: Option<AllowedMention>,
components: Option<Vec<Component>>,
flags: Option<MessageFlags>,
files: Option<Vec<u8>>,
payload_json: Option<String>,
attachments: Option<Vec<Attachment>>,
pub content: Option<String>,
pub embeds: Option<Vec<Embed>>,
pub embed: Option<Embed>,
pub allowed_mentions: Option<AllowedMention>,
pub components: Option<Vec<Component>>,
pub flags: Option<MessageFlags>,
pub files: Option<Vec<u8>>,
pub payload_json: Option<String>,
pub attachments: Option<Vec<Attachment>>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
pub struct ReactionQuerySchema {
pub after: Option<Snowflake>,
pub limit: Option<u32>,
#[serde(rename = "type")]
pub reaction_type: Option<ReactionType>
}

View File

@ -2,11 +2,11 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::num::ParseIntError;
use std::str::FromStr;
use bitflags::bitflags;
use serde::{Deserialize, Serialize};
#[cfg(feature = "sqlx")]
use sqlx::{{Decode, Encode, MySql}, database::{HasArguments, HasValueRef}, encode::IsNull, error::BoxDynError, mysql::MySqlValueRef};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::types::UserFlags;
bitflags! {
/// Rights are instance-wide, per-user permissions for everything you may perform on the instance,
@ -18,7 +18,7 @@ bitflags! {
///
/// # Reference
/// See <https://docs.spacebar.chat/setup/server/security/rights/>
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, chorus_macros::SqlxBitFlags)]
pub struct Rights: u64 {
/// All rights
const OPERATOR = 1 << 0;
@ -132,33 +132,28 @@ bitflags! {
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Type<MySql> for Rights {
fn type_info() -> <sqlx::MySql as sqlx::Database>::TypeInfo {
u64::type_info()
}
impl FromStr for Rights {
type Err = ParseIntError;
fn compatible(ty: &<sqlx::MySql as sqlx::Database>::TypeInfo) -> bool {
u64::compatible(ty)
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<u64>().map(Rights::from_bits).map(|f| f.unwrap_or(Rights::empty()))
}
}
#[cfg(feature = "sqlx")]
impl<'q> Encode<'q, MySql> for Rights {
fn encode_by_ref(&self, buf: &mut <MySql as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
<u64 as Encode<MySql>>::encode_by_ref(&self.0.0, buf)
impl Serialize for Rights {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.bits().to_string())
}
}
#[cfg(feature = "sqlx")]
impl<'r> Decode<'r, MySql> for Rights {
fn decode(value: <MySql as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
let raw = <u64 as Decode<MySql>>::decode(value)?;
Ok(Rights::from_bits(raw).unwrap())
impl<'de> Deserialize<'de> for Rights {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
let s = String::deserialize(deserializer)?.parse::<u64>().map_err(serde::de::Error::custom)?;
Ok(Rights::from_bits(s).unwrap())
}
}
impl Rights {
pub fn any(&self, permission: Rights, check_operator: bool) -> bool {
(check_operator && self.contains(Rights::OPERATOR)) || self.contains(permission)

View File

@ -8,6 +8,9 @@ use std::{
};
use chrono::{DateTime, TimeZone, Utc};
use sqlx::{MySql, TypeInfo};
use sqlx::database::HasArguments;
use sqlx::encode::IsNull;
#[cfg(feature = "sqlx")]
use sqlx::Type;
@ -19,8 +22,6 @@ const EPOCH: i64 = 1420070400000;
/// # Reference
/// See <https://discord.com/developers/docs/reference#snowflakes>
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "sqlx", derive(Type))]
#[cfg_attr(feature = "sqlx", sqlx(transparent))]
pub struct Snowflake(pub u64);
impl Snowflake {
@ -102,6 +103,24 @@ impl<'de> serde::Deserialize<'de> for Snowflake {
}
}
impl sqlx::Type<sqlx::MySql> for Snowflake {
fn type_info() -> <sqlx::MySql as sqlx::Database>::TypeInfo {
<String as sqlx::Type<sqlx::MySql>>::type_info()
}
}
impl<'q> sqlx::Encode<'q, sqlx::MySql> for Snowflake {
fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
<String as sqlx::Encode<'q, sqlx::MySql>>::encode_by_ref(&self.0.to_string(), buf)
}
}
impl<'d> sqlx::Decode<'d, sqlx::MySql> for Snowflake {
fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'d>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
<String as sqlx::Decode<'d, sqlx::MySql>>::decode(value).map(|s| s.parse::<u64>().map(Snowflake).unwrap())
}
}
#[cfg(test)]
mod test {
use chrono::{DateTime, Utc};