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