use std::error::Error;
use std::fmt;
#[derive(Debug)]
pub struct ConfigBuildError(String);
impl fmt::Display for ConfigBuildError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Config build error: {}", self.0)
}
}
impl Error for ConfigBuildError {}
#[derive(Clone)]
pub struct Config {
api_key: String,
timeout: Option<u64>,
}
impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Config")
.field("api_key", &"***REDACTED***")
.field("timeout", &self.timeout)
.finish()
}
}
impl Config {
#[must_use]
pub fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
#[must_use]
pub fn api_key(&self) -> &str {
&self.api_key
}
#[must_use]
pub fn timeout(&self) -> Option<u64> {
self.timeout
}
}
#[derive(Default)]
pub struct ConfigBuilder {
api_key: Option<String>,
timeout: Option<u64>,
}
impl fmt::Debug for ConfigBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConfigBuilder")
.field("api_key", &self.api_key.as_ref().map(|_| "***REDACTED***"))
.field("timeout", &self.timeout)
.finish()
}
}
impl ConfigBuilder {
#[must_use]
pub fn api_key(mut self, api_key: impl Into<String>) -> Self {
self.api_key = Some(api_key.into());
self
}
#[must_use]
pub fn timeout(mut self, seconds: u64) -> Self {
self.timeout = Some(seconds);
self
}
pub fn build(self) -> Result<Config, ConfigBuildError> {
let api_key = self
.api_key
.ok_or_else(|| ConfigBuildError("missing required field: api_key".to_string()))?;
Ok(Config {
api_key,
timeout: self.timeout,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_builder_with_all_fields() {
let config = Config::builder()
.api_key("test_key")
.timeout(30)
.build()
.unwrap();
assert_eq!(config.api_key(), "test_key");
assert_eq!(config.timeout(), Some(30));
}
#[test]
fn test_config_builder_missing_api_key() {
let result = Config::builder().timeout(30).build();
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.0, "missing required field: api_key");
}
#[test]
fn test_config_builder_only_api_key() {
let config = Config::builder().api_key("test_key").build().unwrap();
assert_eq!(config.api_key(), "test_key");
assert_eq!(config.timeout(), None);
}
}