scsys_config/services/database/
db_uri.rs

1/*
2    Appellation: connection <module>
3    Contrib: @FL03
4*/
5
6/// A standard database connection url schema
7#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8#[cfg_attr(
9    feature = "serde",
10    derive(serde::Deserialize, serde::Serialize),
11    serde(default, rename_all = "snake_case")
12)]
13pub struct DatabaseUriSchema {
14    pub(crate) prefix: String,
15    pub(crate) host: String,
16    pub(crate) port: u16,
17    pub(crate) user: String,
18    pub(crate) password: String,
19    pub(crate) database: String,
20}
21
22impl DatabaseUriSchema {
23    pub fn from_parts(
24        prefix: impl ToString,
25        host: impl ToString,
26        port: u16,
27        user: impl ToString,
28        password: impl ToString,
29        database: impl ToString,
30    ) -> Self {
31        Self {
32            prefix: prefix.to_string(),
33            host: host.to_string(),
34            port,
35            user: user.to_string(),
36            password: password.to_string(),
37            database: database.to_string(),
38        }
39    }
40    /// returns a new instance pre-configured with the postgresql prefix.
41    pub fn postgresql(
42        host: impl ToString,
43        port: u16,
44        user: impl ToString,
45        password: impl ToString,
46        database: impl ToString,
47    ) -> Self {
48        Self::from_parts("postgresql", host, port, user, password, database)
49    }
50    /// returns a string representation of the database URL.
51    pub fn to_string(&self) -> String {
52        format!(
53            "{prefix}://{user}:{password}@{host}:{port}/{database}",
54            prefix = self.prefix,
55            user = self.user,
56            password = self.password,
57            host = self.host,
58            port = self.port,
59            database = self.database
60        )
61    }
62    /// returns a reference to the driver
63    pub const fn prefix(&self) -> &String {
64        &self.prefix
65    }
66    /// returns a mutable reference to the driver
67    pub const fn prefix_mut(&mut self) -> &mut String {
68        &mut self.prefix
69    }
70    /// returns a reference to the host
71    #[inline]
72    pub fn host(&self) -> &String {
73        &self.host
74    }
75    /// returns a mutable reference to the host
76    pub const fn host_mut(&mut self) -> &mut String {
77        &mut self.host
78    }
79    /// returns a copy of the configured port
80    pub const fn port(&self) -> u16 {
81        self.port
82    }
83    /// returns a mutable reference to the port
84    pub const fn port_mut(&mut self) -> &mut u16 {
85        &mut self.port
86    }
87    /// returns a reference to the username
88    pub const fn user(&self) -> &String {
89        &self.user
90    }
91    /// returns a mutable reference to the username
92    pub const fn user_mut(&mut self) -> &mut String {
93        &mut self.user
94    }
95    /// returns a reference to the password
96    pub const fn password(&self) -> &String {
97        &self.password
98    }
99    /// returns a mutable reference to the password
100    pub const fn password_mut(&mut self) -> &mut String {
101        &mut self.password
102    }
103    /// returns a reference to the database name
104    pub const fn database(&self) -> &String {
105        &self.database
106    }
107    /// returns a mutable reference to the database name
108    pub const fn database_mut(&mut self) -> &mut String {
109        &mut self.database
110    }
111    /// update the configured database and return a mutable reference to the current instance
112    #[inline]
113    pub fn set_database<U: ToString>(&mut self, value: U) -> &mut Self {
114        *self.database_mut() = value.to_string();
115        self
116    }
117    /// update the configured hostname and return a mutable reference to the current instance
118    #[inline]
119    pub fn set_host(&mut self, host: impl ToString) -> &mut Self {
120        self.host = host.to_string();
121        self
122    }
123    /// update the configured password and return a mutable reference to the current instance
124    #[inline]
125    pub fn set_password<U: ToString>(&mut self, value: U) -> &mut Self {
126        *self.password_mut() = value.to_string();
127        self
128    }
129    /// update the configured port and return a mutable reference to the current instance
130    #[inline]
131    pub fn set_port(&mut self, port: u16) -> &mut Self {
132        self.port = port;
133        self
134    }
135    /// update the configured prefix and return a mutable reference to the current instance
136    #[inline]
137    pub fn set_prefix<U: ToString>(&mut self, value: U) -> &mut Self {
138        *self.prefix_mut() = value.to_string();
139        self
140    }
141    /// update the configured username and return a mutable reference to the current instance
142    #[inline]
143    pub fn set_user<U: ToString>(&mut self, value: U) -> &mut Self {
144        *self.user_mut() = value.to_string();
145        self
146    }
147    /// consumes the current instance to create another with the given database name
148    pub fn with_database<U: ToString>(self, value: U) -> Self {
149        Self {
150            database: value.to_string(),
151            ..self
152        }
153    }
154    /// consumes the current instance to create another with the given host
155    pub fn with_host<U: ToString>(self, value: U) -> Self {
156        Self {
157            host: value.to_string(),
158            ..self
159        }
160    }
161    /// consumes the current instance to create another with the given password
162    pub fn with_password<U: ToString>(self, value: U) -> Self {
163        Self {
164            password: value.to_string(),
165            ..self
166        }
167    }
168    /// consumes the current instance to create another with the given port
169    pub fn with_port(self, value: u16) -> Self {
170        Self {
171            port: value,
172            ..self
173        }
174    }
175    /// consumes the current instance to create another with the given prefix
176    pub fn with_prefix<U: ToString>(self, value: U) -> Self {
177        Self {
178            prefix: value.to_string(),
179            ..self
180        }
181    }
182    /// consumes the current instance to create another with the given username
183    pub fn with_user<U: ToString>(self, value: U) -> Self {
184        Self {
185            user: value.to_string(),
186            ..self
187        }
188    }
189}
190
191impl Default for DatabaseUriSchema {
192    fn default() -> Self {
193        Self::postgresql("localhost", 5432, "postgres", "postgres", "postgres")
194    }
195}
196
197impl core::fmt::Display for DatabaseUriSchema {
198    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
199        f.write_str(&self.to_string())
200    }
201}
202
203#[cfg(feature = "url")]
204impl core::str::FromStr for DatabaseUriSchema {
205    type Err = crate::ConfigError;
206
207    fn from_str(s: &str) -> Result<Self, Self::Err> {
208        let mut schema: DatabaseUriSchema = Self::default();
209        // parse the URL using the `url` crate
210        let url = url::Url::parse(s)?;
211        // try extracting the components from the URL
212        schema.host = url
213            .host_str()
214            .ok_or(url::ParseError::EmptyHost)?
215            .to_string();
216        schema.port = url.port().unwrap_or(5432);
217        schema.user = url.username().to_string();
218        schema.password = url.password().unwrap_or("").to_string();
219        schema.database = url.path().trim_start_matches('/').to_string();
220        schema.prefix = url.scheme().to_string();
221        // return the parsed object
222        Ok(schema)
223    }
224}