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