atproto_identity/
config.rs

1//! Configuration management for AT Protocol identity operations.
2//!
3//! Environment variable utilities, DNS nameserver configuration, and TLS certificate
4//! bundle management with structured error handling.
5
6use crate::errors::ConfigError;
7use anyhow::Result;
8
9/// Certificate bundle paths for TLS verification.
10/// Contains a collection of file paths to certificate bundles.
11#[derive(Clone)]
12pub struct CertificateBundles(Vec<String>);
13
14/// DNS nameserver IP addresses for domain resolution.
15/// Contains a collection of IP addresses for custom DNS resolution.
16#[derive(Clone)]
17pub struct DnsNameservers(Vec<std::net::IpAddr>);
18
19/// Gets a required environment variable value.
20/// Returns an error if the variable is not set.
21pub fn require_env(name: &str) -> Result<String, ConfigError> {
22    std::env::var(name).map_err(|_| ConfigError::MissingEnvironmentVariable {
23        name: name.to_string(),
24    })
25}
26
27/// Gets an optional environment variable value.
28/// Returns empty string if the variable is not set.
29pub fn optional_env(name: &str) -> String {
30    std::env::var(name).unwrap_or("".to_string())
31}
32
33/// Gets an environment variable value with a fallback default.
34/// Returns the default value if the variable is not set.
35pub fn default_env(name: &str, default_value: &str) -> String {
36    std::env::var(name).unwrap_or(default_value.to_string())
37}
38
39/// Gets the application version from git hash or package version.
40/// Returns the git hash if available, otherwise falls back to cargo package version.
41pub fn version() -> Result<String, ConfigError> {
42    option_env!("GIT_HASH")
43        .or(option_env!("CARGO_PKG_VERSION"))
44        .map(|val| val.to_string())
45        .ok_or(ConfigError::VersionNotAvailable)
46}
47
48impl TryFrom<String> for CertificateBundles {
49    type Error = anyhow::Error;
50    fn try_from(value: String) -> Result<Self, Self::Error> {
51        Ok(Self(
52            value
53                .split(';')
54                .filter_map(|s| {
55                    if s.is_empty() {
56                        None
57                    } else {
58                        Some(s.to_string())
59                    }
60                })
61                .collect::<Vec<String>>(),
62        ))
63    }
64}
65impl AsRef<Vec<String>> for CertificateBundles {
66    fn as_ref(&self) -> &Vec<String> {
67        &self.0
68    }
69}
70
71impl TryFrom<String> for DnsNameservers {
72    type Error = anyhow::Error;
73    fn try_from(value: String) -> Result<Self, Self::Error> {
74        // Allow empty value for default DNS configuration
75        if value.is_empty() {
76            return Ok(Self(Vec::new()));
77        }
78
79        let nameservers = value
80            .split(';')
81            .map(|s| s.trim())
82            .filter(|s| !s.is_empty())
83            .map(|s| {
84                s.parse::<std::net::IpAddr>().map_err(|_| {
85                    anyhow::Error::from(ConfigError::InvalidNameserverIP {
86                        value: s.to_string(),
87                    })
88                })
89            })
90            .collect::<Result<Vec<std::net::IpAddr>, _>>()?;
91
92        Ok(Self(nameservers))
93    }
94}
95
96impl AsRef<Vec<std::net::IpAddr>> for DnsNameservers {
97    fn as_ref(&self) -> &Vec<std::net::IpAddr> {
98        &self.0
99    }
100}