revolt_database/util/
reference.rs

1use std::str::FromStr;
2
3use revolt_result::Result;
4#[cfg(feature = "rocket-impl")]
5use rocket::request::FromParam;
6#[cfg(feature = "rocket-impl")]
7use schemars::{
8    schema::{InstanceType, Schema, SchemaObject, SingleOrVec},
9    JsonSchema,
10};
11
12use crate::{
13    Bot, Channel, Database, Emoji, Invite, Member, Message, Server, ServerBan, User, Webhook,
14};
15
16/// Reference to some object in the database
17#[derive(Serialize, Deserialize)]
18pub struct Reference {
19    /// Id of object
20    pub id: String,
21}
22
23impl Reference {
24    /// Create a Ref from an unchecked string
25    pub fn from_unchecked(id: String) -> Reference {
26        Reference { id }
27    }
28
29    /// Fetch ban from Ref
30    pub async fn as_ban(&self, db: &Database, server: &str) -> Result<ServerBan> {
31        db.fetch_ban(server, &self.id).await
32    }
33
34    /// Fetch bot from Ref
35    pub async fn as_bot(&self, db: &Database) -> Result<Bot> {
36        db.fetch_bot(&self.id).await
37    }
38
39    /// Fetch emoji from Ref
40    pub async fn as_emoji(&self, db: &Database) -> Result<Emoji> {
41        db.fetch_emoji(&self.id).await
42    }
43
44    /// Fetch channel from Ref
45    pub async fn as_channel(&self, db: &Database) -> Result<Channel> {
46        db.fetch_channel(&self.id).await
47    }
48
49    /// Fetch invite from Ref or create invite to server if discoverable
50    pub async fn as_invite(&self, db: &Database) -> Result<Invite> {
51        if ulid::Ulid::from_str(&self.id).is_ok() {
52            let server = self.as_server(db).await?;
53            if !server.discoverable {
54                return Err(create_error!(NotFound));
55            }
56
57            Ok(Invite::Server {
58                code: self.id.to_string(),
59                server: server.id,
60                creator: server.owner,
61                channel: server
62                    .channels
63                    .into_iter()
64                    .next()
65                    .ok_or(create_error!(NotFound))?,
66            })
67        } else {
68            db.fetch_invite(&self.id).await
69        }
70    }
71
72    /// Fetch message from Ref
73    pub async fn as_message(&self, db: &Database) -> Result<Message> {
74        db.fetch_message(&self.id).await
75    }
76
77    /// Fetch message from Ref and validate channel
78    pub async fn as_message_in_channel(&self, db: &Database, channel: &str) -> Result<Message> {
79        let msg = db.fetch_message(&self.id).await?;
80        if msg.channel != channel {
81            return Err(create_error!(NotFound));
82        }
83
84        Ok(msg)
85    }
86
87    /// Fetch member from Ref
88    pub async fn as_member(&self, db: &Database, server: &str) -> Result<Member> {
89        db.fetch_member(server, &self.id).await
90    }
91
92    /// Fetch server from Ref
93    pub async fn as_server(&self, db: &Database) -> Result<Server> {
94        db.fetch_server(&self.id).await
95    }
96
97    /// Fetch user from Ref
98    pub async fn as_user(&self, db: &Database) -> Result<User> {
99        db.fetch_user(&self.id).await
100    }
101
102    /// Fetch webhook from Ref
103    pub async fn as_webhook(&self, db: &Database) -> Result<Webhook> {
104        db.fetch_webhook(&self.id).await
105    }
106}
107
108#[cfg(feature = "rocket-impl")]
109impl<'r> FromParam<'r> for Reference {
110    type Error = &'r str;
111
112    fn from_param(param: &'r str) -> Result<Self, Self::Error> {
113        Ok(Reference::from_unchecked(param.into()))
114    }
115}
116
117#[cfg(feature = "rocket-impl")]
118impl JsonSchema for Reference {
119    fn schema_name() -> String {
120        "Id".to_string()
121    }
122
123    fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> Schema {
124        Schema::Object(SchemaObject {
125            instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
126            ..Default::default()
127        })
128    }
129}