1use std::error::Error;
4use std::fmt;
5
6#[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#[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 #[must_use]
37 pub fn builder() -> ConfigBuilder {
38 ConfigBuilder::default()
39 }
40
41 #[must_use]
43 pub fn api_key(&self) -> &str {
44 &self.api_key
45 }
46
47 #[must_use]
49 pub fn timeout(&self) -> Option<u64> {
50 self.timeout
51 }
52}
53
54#[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 #[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 #[must_use]
80 pub fn timeout(mut self, seconds: u64) -> Self {
81 self.timeout = Some(seconds);
82 self
83 }
84
85 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}