Skip to main content

jules_core/config/
mod.rs

1//! Config module.
2
3use std::error::Error;
4use std::fmt;
5
6/// An error that can occur when building a [`Config`].
7#[derive(Debug)]
8pub struct ConfigBuildError(String);
9
10impl fmt::Display for ConfigBuildError {
11    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12        write!(f, "Config build error: {}", self.0)
13    }
14}
15
16impl Error for ConfigBuildError {}
17
18/// The configuration used to build a client.
19#[derive(Clone)]
20pub struct Config {
21    api_key: String,
22    timeout: Option<u64>,
23}
24
25impl fmt::Debug for Config {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        f.debug_struct("Config")
28            .field("api_key", &"***REDACTED***")
29            .field("timeout", &self.timeout)
30            .finish()
31    }
32}
33
34impl Config {
35    /// Creates a new [`ConfigBuilder`] to construct a [`Config`].
36    #[must_use]
37    pub fn builder() -> ConfigBuilder {
38        ConfigBuilder::default()
39    }
40
41    /// Returns the API key.
42    #[must_use]
43    pub fn api_key(&self) -> &str {
44        &self.api_key
45    }
46
47    /// Returns the timeout in seconds, if configured.
48    #[must_use]
49    pub fn timeout(&self) -> Option<u64> {
50        self.timeout
51    }
52}
53
54/// A builder for constructing a [`Config`].
55#[derive(Default)]
56pub struct ConfigBuilder {
57    api_key: Option<String>,
58    timeout: Option<u64>,
59}
60
61impl fmt::Debug for ConfigBuilder {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.debug_struct("ConfigBuilder")
64            .field("api_key", &self.api_key.as_ref().map(|_| "***REDACTED***"))
65            .field("timeout", &self.timeout)
66            .finish()
67    }
68}
69
70impl ConfigBuilder {
71    /// Sets the API key for the configuration.
72    #[must_use]
73    pub fn api_key(mut self, api_key: impl Into<String>) -> Self {
74        self.api_key = Some(api_key.into());
75        self
76    }
77
78    /// Sets the timeout in seconds.
79    #[must_use]
80    pub fn timeout(mut self, seconds: u64) -> Self {
81        self.timeout = Some(seconds);
82        self
83    }
84
85    /// Builds the [`Config`] from the provided configuration.
86    ///
87    /// # Errors
88    ///
89    /// Returns a [`ConfigBuildError`] if a required field is missing.
90    pub fn build(self) -> Result<Config, ConfigBuildError> {
91        let api_key = self
92            .api_key
93            .ok_or_else(|| ConfigBuildError("missing required field: api_key".to_string()))?;
94
95        Ok(Config {
96            api_key,
97            timeout: self.timeout,
98        })
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_config_builder_with_all_fields() {
108        let config = Config::builder()
109            .api_key("test_key")
110            .timeout(30)
111            .build()
112            .unwrap();
113
114        assert_eq!(config.api_key(), "test_key");
115        assert_eq!(config.timeout(), Some(30));
116    }
117
118    #[test]
119    fn test_config_builder_missing_api_key() {
120        let result = Config::builder().timeout(30).build();
121        assert!(result.is_err());
122        let err = result.unwrap_err();
123        assert_eq!(err.0, "missing required field: api_key");
124    }
125
126    #[test]
127    fn test_config_builder_only_api_key() {
128        let config = Config::builder().api_key("test_key").build().unwrap();
129
130        assert_eq!(config.api_key(), "test_key");
131        assert_eq!(config.timeout(), None);
132    }
133}