Skip to main content

ferrule_sql/
url.rs

1use crate::error::SqlError;
2use indexmap::IndexMap;
3use secrecy::SecretString;
4use url::Url;
5
6/// Parsed database connection URL.
7#[derive(Debug, Clone)]
8pub struct DatabaseUrl {
9    raw: String,
10    parsed: Url,
11}
12
13impl DatabaseUrl {
14    /// Parse a raw connection string.
15    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        // Drop the leading '/' from the path component
49        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    /// Query parameters as an ordered map.
57    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    /// Return a redacted display string for logging.
65    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    /// Return the full raw URL string.
72    pub fn as_str(&self) -> &str {
73        &self.raw
74    }
75
76    /// Return the raw connection string.
77    pub fn raw(&self) -> &str {
78        &self.raw
79    }
80}