grant/config/
connection.rs

1use anyhow::Result;
2use envmnt::{ExpandOptions, ExpansionType};
3use log::warn;
4use serde::{Deserialize, Serialize};
5
6/// Connection type. Supported values: Postgres
7#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
8pub enum ConnectionType {
9    #[serde(rename = "postgres")]
10    Postgres,
11}
12
13/// Connection configuration section.
14/// The user on the connection should have the permission to grant privileges.
15///
16/// For example:
17/// ```yaml
18/// connection:
19///   type: postgres
20///   url: postgres://user:password@host:port/database
21/// ```
22///
23/// The connection type is required.
24#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
25pub struct Connection {
26    #[serde(rename = "type")]
27    pub type_: ConnectionType,
28    pub url: String,
29}
30
31impl Connection {
32    pub fn validate(&self) -> Result<()> {
33        match self.type_ {
34            ConnectionType::Postgres => Ok(()),
35        }
36    }
37
38    /// Expand environment variables in the `url` field.
39    /// For example: postgres://user:${PASSWORD}@host:port/database
40    pub fn expand_env_vars(&self) -> Result<Self> {
41        let mut connection = self.clone();
42
43        let options = ExpandOptions {
44            expansion_type: Some(ExpansionType::UnixBracketsWithDefaults),
45            default_to_empty: false,
46        };
47
48        connection.url = envmnt::expand(&self.url, Some(options));
49
50        // Warning if still have environment variables in the `url` field.
51        // Most likely, the user forgot to export the environment variables.
52        if connection.url.contains("${") {
53            warn!(
54                "The connection url may not have fully expanded environment variables: {}",
55                connection.url
56            );
57        }
58
59        Ok(connection)
60    }
61}
62
63/// Implement default values for connection type and url.
64impl Default for Connection {
65    fn default() -> Self {
66        Self {
67            type_: ConnectionType::Postgres,
68            url: "postgres://postgres:postgres@localhost:5432/postgres".to_string(),
69        }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_connection_validate() {
79        let connection = Connection {
80            type_: ConnectionType::Postgres,
81            url: "postgres://postgres:postgres@localhost:5432/postgres".to_string(),
82        };
83        assert!(connection.validate().is_ok());
84    }
85
86    #[test]
87    fn test_connection_expand_env_vars() {
88        // backup the original env variables
89        let original_env = envmnt::get_or("PASSWORD", "");
90        envmnt::set("PASSWORD", "postgres");
91
92        let connection = Connection {
93            type_: ConnectionType::Postgres,
94            url: "postgres://user:${PASSWORD}@host:port/database".to_string(),
95        };
96        let expanded_connection = connection.expand_env_vars().unwrap();
97        assert_eq!(
98            expanded_connection.url,
99            "postgres://user:postgres@host:port/database"
100        );
101
102        // restore the original env variables
103        envmnt::set("PASSWORD", original_env);
104    }
105}