diff --git a/crates/core/models/Cargo.toml b/crates/core/models/Cargo.toml index cd7c2dc4..b6ae583d 100644 --- a/crates/core/models/Cargo.toml +++ b/crates/core/models/Cargo.toml @@ -11,19 +11,18 @@ description = "Revolt Backend: API Models" [features] serde = [ "dep:serde" ] schemas = [ "dep:schemars" ] -from_database = [ "revolt-database", "revolt-presence" ] +validator = [ "dep:validator" ] +partials = [ "dep:revolt_optional_struct", "serde", "schemas" ] -redis-is-patched = [ "revolt-presence/redis-is-patched" ] - -default = [ "serde", "from_database" ] +default = [ "serde", "partials" ] [dependencies] -# Repo -revolt-database = { version = "0.0.2", path = "../database", optional = true } -revolt-presence = { version = "0.0.2", path = "../presence", optional = true } - # Serialisation +revolt_optional_struct = { version = "0.2.0", optional = true } serde = { version = "1", features = ["derive"], optional = true } # Spec Generation schemars = { version = "0.8.8", optional = true } + +# Validation +validator = { version = "0.16.0", optional = true, features = ["derive"] } diff --git a/crates/core/models/src/lib.rs b/crates/core/models/src/lib.rs index 03f6c818..0812d0f9 100644 --- a/crates/core/models/src/lib.rs +++ b/crates/core/models/src/lib.rs @@ -6,6 +6,13 @@ extern crate serde; #[macro_use] extern crate schemars; +#[cfg(feature = "partials")] +#[macro_use] +extern crate revolt_optional_struct; + +#[cfg(feature = "validator")] +pub use validator; + macro_rules! auto_derived { ( $( $item:item )+ ) => { $( @@ -17,6 +24,28 @@ macro_rules! auto_derived { }; } +#[cfg(feature = "partials")] +macro_rules! auto_derived_partial { + ( $item:item, $name:expr ) => { + #[derive( + OptionalStruct, Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema, + )] + #[optional_derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] + #[optional_name = $name] + #[opt_skip_serializing_none] + #[opt_some_priority] + $item + }; +} + +#[cfg(not(feature = "partials"))] +macro_rules! auto_derived_partial { + ( $item:item, $name:expr ) => { + #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] + $item + }; +} + pub mod v0; /// Utility function to check if a boolean value is false diff --git a/crates/core/models/src/v0/account_strikes.rs b/crates/core/models/src/v0/account_strikes.rs index d402d2f1..88174ee1 100644 --- a/crates/core/models/src/v0/account_strikes.rs +++ b/crates/core/models/src/v0/account_strikes.rs @@ -2,7 +2,7 @@ auto_derived!( /// Account Strike pub struct AccountStrike { /// Strike Id - #[serde(rename = "_id")] + #[cfg_attr(feature = "serde", serde(rename = "_id"))] pub id: String, /// Id of reported user pub user_id: String, @@ -26,14 +26,3 @@ auto_derived!( pub reason: String, } ); - -#[cfg(feature = "from_database")] -impl From for AccountStrike { - fn from(value: revolt_database::AccountStrike) -> Self { - AccountStrike { - id: value.id, - user_id: value.user_id, - reason: value.reason, - } - } -} diff --git a/crates/core/models/src/v0/bots.rs b/crates/core/models/src/v0/bots.rs index 43a268b8..354ca555 100644 --- a/crates/core/models/src/v0/bots.rs +++ b/crates/core/models/src/v0/bots.rs @@ -4,11 +4,11 @@ auto_derived!( /// Bot pub struct Bot { /// Bot Id - #[serde(rename = "_id")] + #[cfg_attr(feature = "serde", serde(rename = "_id"))] pub id: String, /// User Id of the bot owner - #[serde(rename = "owner")] + #[cfg_attr(feature = "serde", serde(rename = "owner"))] pub owner_id: String, /// Token used to authenticate requests for this bot pub token: String, @@ -66,16 +66,16 @@ auto_derived!( pub struct PublicBot { /// Bot Id #[serde(rename = "_id")] - id: String, + pub id: String, /// Bot Username - username: String, + pub username: String, /// Profile Avatar #[serde(skip_serializing_if = "String::is_empty")] - avatar: String, + pub avatar: String, /// Profile Description #[serde(skip_serializing_if = "String::is_empty")] - description: String, + pub description: String, } /// Bot Response @@ -86,39 +86,3 @@ auto_derived!( pub user: User, } ); - -#[cfg(feature = "from_database")] -impl PublicBot { - pub fn from(bot: revolt_database::Bot, user: revolt_database::User) -> Self { - #[cfg(debug_assertions)] - assert_eq!(bot.id, user.id); - - PublicBot { - id: bot.id, - username: user.username, - avatar: user.avatar.map(|x| x.id).unwrap_or_default(), - description: user - .profile - .map(|profile| profile.content) - .unwrap_or_default(), - } - } -} - -#[cfg(feature = "from_database")] -impl From for Bot { - fn from(value: revolt_database::Bot) -> Self { - Bot { - id: value.id, - owner_id: value.owner, - token: value.token, - public: value.public, - analytics: value.analytics, - discoverable: value.discoverable, - interactions_url: value.interactions_url, - terms_of_service_url: value.terms_of_service_url, - privacy_policy_url: value.privacy_policy_url, - flags: value.flags.unwrap_or_default() as u32, - } - } -} diff --git a/crates/core/models/src/v0/channel_webhooks.rs b/crates/core/models/src/v0/channel_webhooks.rs new file mode 100644 index 00000000..93f5649b --- /dev/null +++ b/crates/core/models/src/v0/channel_webhooks.rs @@ -0,0 +1,90 @@ +use super::File; + +auto_derived_partial!( + /// Webhook + pub struct Webhook { + /// Webhook Id + pub id: String, + + /// The name of the webhook + pub name: String, + + /// The avatar of the webhook + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub avatar: Option, + + /// The channel this webhook belongs to + pub channel_id: String, + + /// The private token for the webhook + pub token: Option, + }, + "PartialWebhook" +); + +auto_derived!( + /// Information about the webhook bundled with Message + pub struct MessageWebhook { + // The name of the webhook - 1 to 32 chars + pub name: String, + + // The id of the avatar of the webhook, if it has one + pub avatar: Option, + } + + /// New webhook information + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct DataEditWebhook { + /// Webhook name + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))] + pub name: Option, + + /// Avatar ID + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))] + pub avatar: Option, + + /// Fields to remove from webhook + #[cfg_attr(feature = "serde", serde(default))] + pub remove: Vec, + } + + /// Webhook information + pub struct ResponseWebhook { + /// Webhook Id + pub id: String, + + /// Webhook name + pub name: String, + + /// Avatar ID + pub avatar: Option, + + /// The channel this webhook belongs to + pub channel_id: String, + } + + /// Optional fields on webhook object + pub enum FieldsWebhook { + Avatar, + } +); + +impl From for MessageWebhook { + fn from(value: Webhook) -> Self { + MessageWebhook { + name: value.name, + avatar: value.avatar.map(|file| file.id), + } + } +} + +impl From for ResponseWebhook { + fn from(value: Webhook) -> Self { + ResponseWebhook { + id: value.id, + name: value.name, + avatar: value.avatar.map(|file| file.id), + channel_id: value.channel_id, + } + } +} diff --git a/crates/core/models/src/v0/files.rs b/crates/core/models/src/v0/files.rs index 89654b75..df30b216 100644 --- a/crates/core/models/src/v0/files.rs +++ b/crates/core/models/src/v0/files.rs @@ -54,42 +54,3 @@ auto_derived!( Audio, } ); - -#[cfg(feature = "from_database")] -impl From for File { - fn from(value: revolt_database::File) -> Self { - File { - id: value.id, - tag: value.tag, - filename: value.filename, - metadata: value.metadata.into(), - content_type: value.content_type, - size: value.size, - deleted: value.deleted, - reported: value.reported, - message_id: value.message_id, - user_id: value.user_id, - server_id: value.server_id, - object_id: value.object_id, - } - } -} - -#[cfg(feature = "from_database")] -impl From for Metadata { - fn from(value: revolt_database::Metadata) -> Self { - match value { - revolt_database::Metadata::File => Metadata::File, - revolt_database::Metadata::Text => Metadata::Text, - revolt_database::Metadata::Image { width, height } => Metadata::Image { - width: width as usize, - height: height as usize, - }, - revolt_database::Metadata::Video { width, height } => Metadata::Video { - width: width as usize, - height: height as usize, - }, - revolt_database::Metadata::Audio => Metadata::Audio, - } - } -} diff --git a/crates/core/models/src/v0/mod.rs b/crates/core/models/src/v0/mod.rs index 9654fb85..74b2cdac 100644 --- a/crates/core/models/src/v0/mod.rs +++ b/crates/core/models/src/v0/mod.rs @@ -1,9 +1,11 @@ mod account_strikes; mod bots; +mod channel_webhooks; mod files; mod users; pub use account_strikes::*; pub use bots::*; +pub use channel_webhooks::*; pub use files::*; pub use users::*; diff --git a/crates/core/models/src/v0/users.rs b/crates/core/models/src/v0/users.rs index 2671d28b..8224639f 100644 --- a/crates/core/models/src/v0/users.rs +++ b/crates/core/models/src/v0/users.rs @@ -158,109 +158,3 @@ impl CheckRelationship for Vec { RelationshipStatus::None } } - -#[cfg(feature = "from_database")] -impl User { - pub async fn from

(user: revolt_database::User, perspective: P) -> Self - where - P: Into>, - { - let relationship = if let Some(perspective) = perspective.into() { - perspective - .relations - .unwrap_or_default() - .into_iter() - .find(|relationship| relationship.id == user.id) - .map(|relationship| relationship.status.into()) - .unwrap_or_default() - } else { - RelationshipStatus::None - }; - - // do permission stuff here - // TODO: implement permissions =) - let can_see_profile = false; - - Self { - username: user.username, - avatar: user.avatar.map(|file| file.into()), - relations: vec![], - badges: user.badges.unwrap_or_default() as u32, - status: None, - profile: None, - flags: user.flags.unwrap_or_default() as u32, - privileged: user.privileged, - bot: user.bot.map(|bot| bot.into()), - relationship, - online: can_see_profile && revolt_presence::is_online(&user.id).await, - id: user.id, - } - } -} - -#[cfg(feature = "from_database")] -impl From for RelationshipStatus { - fn from(value: revolt_database::RelationshipStatus) -> Self { - match value { - revolt_database::RelationshipStatus::None => RelationshipStatus::None, - revolt_database::RelationshipStatus::User => RelationshipStatus::User, - revolt_database::RelationshipStatus::Friend => RelationshipStatus::Friend, - revolt_database::RelationshipStatus::Outgoing => RelationshipStatus::Outgoing, - revolt_database::RelationshipStatus::Incoming => RelationshipStatus::Incoming, - revolt_database::RelationshipStatus::Blocked => RelationshipStatus::Blocked, - revolt_database::RelationshipStatus::BlockedOther => RelationshipStatus::BlockedOther, - } - } -} - -#[cfg(feature = "from_database")] -impl From for Relationship { - fn from(value: revolt_database::Relationship) -> Self { - Self { - user_id: value.id, - status: value.status.into(), - } - } -} - -#[cfg(feature = "from_database")] -impl From for Presence { - fn from(value: revolt_database::Presence) -> Self { - match value { - revolt_database::Presence::Online => Presence::Online, - revolt_database::Presence::Idle => Presence::Idle, - revolt_database::Presence::Focus => Presence::Focus, - revolt_database::Presence::Busy => Presence::Busy, - revolt_database::Presence::Invisible => Presence::Invisible, - } - } -} - -#[cfg(feature = "from_database")] -impl From for UserStatus { - fn from(value: revolt_database::UserStatus) -> Self { - UserStatus { - text: value.text, - presence: value.presence.map(|presence| presence.into()), - } - } -} - -#[cfg(feature = "from_database")] -impl From for UserProfile { - fn from(value: revolt_database::UserProfile) -> Self { - UserProfile { - content: value.content, - background: value.background.map(|file| file.into()), - } - } -} - -#[cfg(feature = "from_database")] -impl From for BotInformation { - fn from(value: revolt_database::BotInformation) -> Self { - BotInformation { - owner_id: value.owner, - } - } -}