cc_switch/config/
types.rs

1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use std::collections::BTreeMap;
3
4/// Type alias for configuration map
5type ConfigMap = BTreeMap<String, Configuration>;
6/// Type alias for environment variable map
7type EnvMap = BTreeMap<String, String>;
8/// Type alias for JSON value map
9type JsonMap = BTreeMap<String, serde_json::Value>;
10
11/// Represents a Claude API configuration
12///
13/// Contains the components needed to configure Claude API access:
14/// - alias_name: User-friendly identifier for the configuration
15/// - token: API authentication token
16/// - url: Base URL for the API endpoint
17/// - model: Optional custom model name
18/// - small_fast_model: Optional Haiku-class model for background tasks
19#[derive(Serialize, Deserialize, Default, Clone)]
20pub struct Configuration {
21    /// User-friendly alias name for this configuration
22    pub alias_name: String,
23    /// ANTHROPIC_AUTH_TOKEN value (API authentication token)
24    pub token: String,
25    /// ANTHROPIC_BASE_URL value (API endpoint URL)
26    pub url: String,
27    /// ANTHROPIC_MODEL value (custom model name)
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub model: Option<String>,
30    /// ANTHROPIC_SMALL_FAST_MODEL value (Haiku-class model for background tasks)
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub small_fast_model: Option<String>,
33    /// ANTHROPIC_MAX_THINKING_TOKENS value (Maximum thinking tokens limit)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub max_thinking_tokens: Option<u32>,
36    /// API timeout in milliseconds
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub api_timeout_ms: Option<u32>,
39    /// Disable non-essential traffic flag
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub claude_code_disable_nonessential_traffic: Option<u32>,
42    /// Default Sonnet model name
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub anthropic_default_sonnet_model: Option<String>,
45    /// Default Opus model name
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub anthropic_default_opus_model: Option<String>,
48    /// Default Haiku model name
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub anthropic_default_haiku_model: Option<String>,
51}
52
53/// Storage manager for Claude API configurations
54///
55/// Handles persistence and retrieval of multiple API configurations
56/// stored in `~/.cc_auto_switch/configurations.json`
57#[derive(Serialize, Deserialize, Default)]
58pub struct ConfigStorage {
59    /// Map of alias names to configuration objects
60    pub configurations: ConfigMap,
61    /// Custom directory for Claude settings (optional)
62    pub claude_settings_dir: Option<String>,
63}
64
65/// Claude settings manager for API configuration
66///
67/// Manages the Claude settings.json file to control Claude's API configuration
68/// Handles environment variables and preserves other settings
69#[derive(Default, Clone)]
70#[allow(dead_code)]
71pub struct ClaudeSettings {
72    /// Environment variables map (ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL, ANTHROPIC_MODEL, ANTHROPIC_SMALL_FAST_MODEL)
73    pub env: EnvMap,
74    /// Other settings to preserve when modifying API configuration
75    pub other: JsonMap,
76}
77
78impl Serialize for ClaudeSettings {
79    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80    where
81        S: Serializer,
82    {
83        use serde::ser::SerializeMap;
84
85        let mut map = serializer.serialize_map(Some(
86            self.other.len() + if self.env.is_empty() { 0 } else { 1 },
87        ))?;
88
89        // Serialize env field only if it has content
90        if !self.env.is_empty() {
91            map.serialize_entry("env", &self.env)?;
92        }
93
94        // Serialize other fields
95        for (key, value) in &self.other {
96            map.serialize_entry(key, value)?;
97        }
98
99        map.end()
100    }
101}
102
103impl<'de> Deserialize<'de> for ClaudeSettings {
104    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
105    where
106        D: Deserializer<'de>,
107    {
108        #[derive(Deserialize)]
109        struct ClaudeSettingsHelper {
110            #[serde(default)]
111            env: EnvMap,
112            #[serde(flatten)]
113            other: JsonMap,
114        }
115
116        let helper = ClaudeSettingsHelper::deserialize(deserializer)?;
117        Ok(ClaudeSettings {
118            env: helper.env,
119            other: helper.other,
120        })
121    }
122}
123
124/// Parameters for adding a new configuration
125#[allow(dead_code)]
126pub struct AddCommandParams {
127    pub alias_name: String,
128    pub token: Option<String>,
129    pub url: Option<String>,
130    pub model: Option<String>,
131    pub small_fast_model: Option<String>,
132    pub max_thinking_tokens: Option<u32>,
133    pub api_timeout_ms: Option<u32>,
134    pub claude_code_disable_nonessential_traffic: Option<u32>,
135    pub anthropic_default_sonnet_model: Option<String>,
136    pub anthropic_default_opus_model: Option<String>,
137    pub anthropic_default_haiku_model: Option<String>,
138    pub force: bool,
139    pub interactive: bool,
140    pub token_arg: Option<String>,
141    pub url_arg: Option<String>,
142    pub from_file: Option<String>,
143}