Skip to main content

echoip/
config.rs

1use clap::Parser;
2
3#[derive(Parser, Debug, Clone)]
4#[command(name = "echoip", about = "IP address lookup service")]
5pub struct Config {
6    /// Path to GeoIP country database
7    #[arg(short = 'f', long = "country-db", default_value = "")]
8    pub country_db: String,
9
10    /// Path to GeoIP city database
11    #[arg(short = 'c', long = "city-db", default_value = "")]
12    pub city_db: String,
13
14    /// Path to GeoIP ASN database
15    #[arg(short = 'a', long = "asn-db", default_value = "")]
16    pub asn_db: String,
17
18    /// Listening address
19    #[arg(short = 'l', long = "listen", default_value = ":8080")]
20    pub listen: String,
21
22    /// Perform reverse hostname lookups
23    #[arg(short = 'r', long = "reverse-lookup")]
24    pub reverse_lookup: bool,
25
26    /// Enable port lookup
27    #[arg(short = 'p', long = "port-lookup")]
28    pub port_lookup: bool,
29
30    /// Path to template directory
31    #[arg(short = 't', long = "template", default_value = "html")]
32    pub template: String,
33
34    /// Size of response cache (0 to disable)
35    #[arg(short = 'C', long = "cache-size", default_value_t = 0)]
36    pub cache_size: usize,
37
38    /// Enable profiling/debug handlers
39    #[arg(short = 'P', long = "profile")]
40    pub profile: bool,
41
42    /// Show sponsor logo
43    #[arg(short = 's', long = "sponsor")]
44    pub sponsor: bool,
45
46    /// Headers to trust for remote IP (repeatable)
47    #[arg(short = 'H', long = "trusted-header")]
48    pub trusted_headers: Vec<String>,
49
50    /// Path to ip66.dev MMDB database
51    #[arg(long = "ip66-db")]
52    pub ip66_db: Option<String>,
53
54    /// Directory for auto-downloaded databases
55    #[arg(short = 'd', long = "data-dir", default_value = "data")]
56    pub data_dir: String,
57
58    /// Auto-update interval in hours (0 to disable periodic updates)
59    #[arg(long = "update-interval", default_value_t = 0)]
60    pub update_interval: u64,
61
62    /// Disable automatic database download on startup
63    #[arg(long = "no-auto-download")]
64    pub no_auto_download: bool,
65}
66
67impl Config {
68    pub fn listen_addr(&self) -> String {
69        let listen = &self.listen;
70        if listen.starts_with(':') {
71            format!("0.0.0.0{listen}")
72        } else {
73            listen.clone()
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_listen_addr_with_colon() {
84        let config = Config {
85            listen: ":8080".into(),
86            country_db: String::new(),
87            city_db: String::new(),
88            asn_db: String::new(),
89            reverse_lookup: false,
90            port_lookup: false,
91            template: String::new(),
92            cache_size: 0,
93            profile: false,
94            sponsor: false,
95            trusted_headers: vec![],
96            ip66_db: None,
97            data_dir: "data".into(),
98            update_interval: 0,
99            no_auto_download: true,
100        };
101        assert_eq!(config.listen_addr(), "0.0.0.0:8080");
102    }
103
104    #[test]
105    fn test_listen_addr_full() {
106        let config = Config {
107            listen: "127.0.0.1:3000".into(),
108            country_db: String::new(),
109            city_db: String::new(),
110            asn_db: String::new(),
111            reverse_lookup: false,
112            port_lookup: false,
113            template: String::new(),
114            cache_size: 0,
115            profile: false,
116            sponsor: false,
117            trusted_headers: vec![],
118            ip66_db: None,
119            data_dir: "data".into(),
120            update_interval: 0,
121            no_auto_download: true,
122        };
123        assert_eq!(config.listen_addr(), "127.0.0.1:3000");
124    }
125}