1use crate::error::SqlError;
2use indexmap::IndexMap;
3use secrecy::SecretString;
4use url::Url;
5
6#[derive(Debug, Clone)]
8pub struct DatabaseUrl {
9 raw: String,
10 parsed: Url,
11}
12
13impl DatabaseUrl {
14 pub fn parse(raw: &str) -> Result<Self, SqlError> {
16 let parsed = Url::parse(raw).map_err(|e| SqlError::InvalidUrl(format!("{e}")))?;
17 Ok(Self {
18 raw: raw.to_string(),
19 parsed,
20 })
21 }
22
23 pub fn scheme(&self) -> &str {
24 self.parsed.scheme()
25 }
26
27 pub fn username(&self) -> &str {
28 self.parsed.username()
29 }
30
31 pub fn password(&self) -> Option<SecretString> {
32 self.parsed.password().map(|p| SecretString::new(p.into()))
33 }
34
35 pub fn host(&self) -> Option<&str> {
36 self.parsed.host_str()
37 }
38
39 pub fn port(&self) -> Option<u16> {
40 self.parsed.port()
41 }
42
43 pub fn path(&self) -> &str {
44 self.parsed.path()
45 }
46
47 pub fn database(&self) -> &str {
48 self.parsed.path().trim_start_matches('/')
50 }
51
52 pub fn set_password(&mut self, password: Option<&str>) {
53 let _ = self.parsed.set_password(password);
54 }
55
56 pub fn params(&self) -> IndexMap<String, String> {
58 self.parsed
59 .query_pairs()
60 .map(|(k, v)| (k.into_owned(), v.into_owned()))
61 .collect()
62 }
63
64 pub fn redacted(&self) -> String {
66 let mut url = self.parsed.clone();
67 let _ = url.set_password(Some("***"));
68 url.to_string()
69 }
70
71 pub fn as_str(&self) -> &str {
73 &self.raw
74 }
75
76 pub fn raw(&self) -> &str {
78 &self.raw
79 }
80}