dns_geolocation_checker/
configs_parser.rs

1#![allow(dead_code)]
2
3use serde::Deserialize;
4use std::{collections::HashMap, fs};
5
6use crate::ip_geo_client::IpGeoProviderType;
7
8/// A struct to hold the parsed config
9#[derive(Default, Debug, Clone, Deserialize)]
10pub struct Config {
11    /// The IP geo provider
12    #[serde(default)]
13    pub ip_geo_provider: IpGeoProviderType,
14    #[serde(default)]
15    pub mmdb_path: Option<String>,
16    /// A map of country codes to their respective subnets
17    pub test_subnets: HashMap<String, RoutingCountryConfig>,
18    /// A list of domains and their respective geo routing
19    pub domain: Vec<DomainConfig>,
20}
21
22/// A struct to hold the domain config
23#[derive(Default, Debug, Clone, Deserialize)]
24pub struct DomainConfig {
25    /// The host of the domain
26    pub host: String,
27    /// A list of country codes to route to
28    pub geo_routing: Vec<String>,
29}
30
31/// A struct to hold the subnets for a country
32#[derive(Default, Debug, Clone, Deserialize)]
33pub struct RoutingCountryConfig {
34    /// A list of subnets
35    pub subnets: Vec<String>,
36}
37
38#[derive(Clone)]
39pub struct ConfigParser<T: for<'a> Deserialize<'a>> {
40    /// The parsed config
41    config: T,
42}
43
44impl<C: for<'a> Deserialize<'a>> ConfigParser<C> {
45    /// Parse the contents from a TOML string
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use dns_geolocation_checker::configs_parser::{ConfigParser, Config};
51    ///
52    /// let test_config = r#"
53    /// [test_subnets]
54    /// us = { subnets = ["44.208.193.0/24"] }
55    ///
56    /// [[domain]]
57    /// host = "google.com"
58    /// geo_routing = ["us"]
59    /// "#;
60    ///
61    /// let config: Config = ConfigParser::parse(test_config.to_string());
62    ///
63    /// assert_eq!(config.domain.len(), 1);
64    /// assert_eq!(config.test_subnets.len(), 1);
65    /// assert_eq!(config.domain[0].host, "google.com");
66    /// assert_eq!(config.test_subnets.get("us").unwrap().subnets[0], "44.208.193.0/24");
67    /// ```
68    pub fn parse(contents: String) -> C {
69        toml::from_str(&contents).unwrap()
70    }
71}
72
73impl ConfigParser<Config> {
74    /// Create a new ConfigParser with the contents of a file
75    pub fn new_with_path<T: ToString>(path: T) -> ConfigParser<Config> {
76        let contents =
77            fs::read_to_string(&path.to_string()).expect("Should have been able to read the file");
78
79        ConfigParser {
80            config: ConfigParser::parse(contents),
81        }
82    }
83
84    /// Get the parsed config
85    pub fn config(&self) -> &Config {
86        &self.config
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use std::fs;
94    const TEMP_DIR_PATH: &'static str = "./temp";
95    const TEMP_PATH: &'static str = "./temp/domain.toml";
96
97    fn setup() {
98        if fs::read_dir(TEMP_DIR_PATH).is_err() {
99            fs::create_dir_all(TEMP_DIR_PATH).expect("Unable to create directory");
100        }
101
102        let test_config = r#"
103            [test_subnets]
104            us = { subnets = ["44.208.193.0/24"] }
105
106            [[domain]]
107            host = "google.com"
108            geo_routing = ["us"]
109        "#
110        .trim();
111
112        fs::write(TEMP_PATH, test_config).expect("Unable to write file");
113    }
114
115    fn teardown() {
116        fs::remove_dir_all(TEMP_DIR_PATH).expect("Unable to remove directory");
117    }
118
119    #[test]
120    fn test_new_with_path() {
121        setup();
122
123        let parser = ConfigParser::new_with_path(TEMP_PATH);
124        let config = parser.config();
125        assert_eq!(config.domain.len(), 1);
126        assert_eq!(config.test_subnets.len(), 1);
127        assert_eq!(config.domain.len(), 1);
128        assert_eq!(config.test_subnets.len(), 1);
129        assert_eq!(config.domain[0].host, "google.com");
130        assert_eq!(
131            config.test_subnets.get("us").unwrap().subnets[0],
132            "44.208.193.0/24"
133        );
134
135        teardown();
136    }
137
138    #[test]
139    fn test_parse() {
140        let test_config = r#"
141            [test_subnets]
142            us = { subnets = ["44.208.193.0/24"] }
143
144            [[domain]]
145            host = "google.com"
146            geo_routing = ["us"]
147        "#;
148
149        let config: Config = ConfigParser::parse(test_config.to_string());
150        assert_eq!(config.domain.len(), 1);
151        assert_eq!(config.test_subnets.len(), 1);
152        assert_eq!(config.domain[0].host, "google.com");
153        assert_eq!(
154            config.test_subnets.get("us").unwrap().subnets[0],
155            "44.208.193.0/24"
156        );
157    }
158}