claude_agent/config/
mod.rs

1//! Pluggable configuration provider system.
2//!
3//! ```rust,no_run
4//! use claude_agent::config::ConfigBuilder;
5//!
6//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
7//! let config = ConfigBuilder::new()
8//!     .env()
9//!     .file(".claude/settings.json")
10//!     .build()
11//!     .await?;
12//! # Ok(())
13//! # }
14//! ```
15
16pub mod cloud;
17pub mod composite;
18pub mod env;
19pub mod file;
20pub mod memory;
21pub mod provider;
22pub mod settings;
23pub mod validator;
24
25pub use cloud::{BedrockConfig, CloudConfig, FoundryConfig, TokenLimits, VertexConfig};
26pub use composite::CompositeConfigProvider;
27pub use env::EnvConfigProvider;
28pub use file::FileConfigProvider;
29pub use memory::MemoryConfigProvider;
30pub use provider::{ConfigProvider, ConfigProviderExt};
31pub use settings::{
32    HookConfig, HooksSettings, NetworkSandboxSettings, PermissionSettings, SandboxSettings,
33    Settings, SettingsLoader, SettingsSource,
34};
35pub use validator::{ConfigValidator, ValueType};
36
37use thiserror::Error;
38
39/// Errors that can occur in configuration operations
40#[derive(Error, Debug)]
41pub enum ConfigError {
42    /// Key not found
43    #[error("Key not found: {key}")]
44    NotFound {
45        /// The key that was not found
46        key: String,
47    },
48
49    /// Invalid configuration value
50    #[error("Invalid value for {key}: {message}")]
51    InvalidValue {
52        /// The key with invalid value
53        key: String,
54        /// Error message
55        message: String,
56    },
57
58    /// Serialization/deserialization error
59    #[error("Serialization error: {0}")]
60    Serialization(#[from] serde_json::Error),
61
62    /// IO error (file operations)
63    #[error("IO error: {0}")]
64    Io(#[from] std::io::Error),
65
66    /// Environment variable error
67    #[error("Environment error: {0}")]
68    Env(#[from] std::env::VarError),
69
70    /// Provider error
71    #[error("Provider error: {message}")]
72    Provider {
73        /// Error message
74        message: String,
75    },
76
77    /// Multiple validation errors
78    #[error("{0}")]
79    ValidationErrors(ValidationErrors),
80}
81
82#[derive(Debug)]
83pub struct ValidationErrors(pub Vec<ConfigError>);
84
85impl std::fmt::Display for ValidationErrors {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(f, "Validation failed: ")?;
88        let msgs: Vec<String> = self.0.iter().map(|e| e.to_string()).collect();
89        write!(f, "{}", msgs.join("; "))
90    }
91}
92
93/// Result type for configuration operations
94pub type ConfigResult<T> = std::result::Result<T, ConfigError>;
95
96/// Configuration builder for fluent API
97pub struct ConfigBuilder {
98    providers: Vec<Box<dyn ConfigProvider>>,
99}
100
101impl ConfigBuilder {
102    /// Create a new configuration builder
103    pub fn new() -> Self {
104        Self {
105            providers: Vec::new(),
106        }
107    }
108
109    /// Add environment variable provider
110    pub fn env(mut self) -> Self {
111        self.providers.push(Box::new(EnvConfigProvider::new()));
112        self
113    }
114
115    /// Add environment variable provider with prefix
116    pub fn env_with_prefix(mut self, prefix: &str) -> Self {
117        self.providers
118            .push(Box::new(EnvConfigProvider::with_prefix(prefix)));
119        self
120    }
121
122    /// Add file provider
123    pub fn file(mut self, path: impl AsRef<std::path::Path>) -> Self {
124        self.providers.push(Box::new(FileConfigProvider::new(
125            path.as_ref().to_path_buf(),
126        )));
127        self
128    }
129
130    /// Add memory provider
131    pub fn memory(mut self, provider: MemoryConfigProvider) -> Self {
132        self.providers.push(Box::new(provider));
133        self
134    }
135
136    /// Add a custom provider
137    pub fn provider(mut self, provider: Box<dyn ConfigProvider>) -> Self {
138        self.providers.push(provider);
139        self
140    }
141
142    /// Build the composite configuration
143    pub async fn build(self) -> ConfigResult<CompositeConfigProvider> {
144        let mut composite = CompositeConfigProvider::new();
145        for provider in self.providers {
146            composite.add_provider(provider);
147        }
148        Ok(composite)
149    }
150}
151
152impl Default for ConfigBuilder {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_config_error_display() {
164        let err = ConfigError::NotFound {
165            key: "api_key".to_string(),
166        };
167        assert!(err.to_string().contains("api_key"));
168    }
169
170    #[test]
171    fn test_config_builder() {
172        let builder = ConfigBuilder::new().env().env_with_prefix("CLAUDE_");
173        assert!(!builder.providers.is_empty());
174    }
175}