use anyhow::{Context, Result};
use std::collections::BTreeMap;
use std::path::PathBuf;
use crate::config::types::Configuration;
type EnvVarMap = BTreeMap<String, String>;
type EnvVarTuple = (String, String);
type EnvVarTuples = Vec<EnvVarTuple>;
#[derive(Default, Clone)]
pub struct EnvironmentConfig {
pub env_vars: EnvVarMap,
}
impl EnvironmentConfig {
pub fn from_config(config: &Configuration) -> Self {
let mut env_vars = EnvVarMap::new();
env_vars.insert("ANTHROPIC_AUTH_TOKEN".to_string(), config.token.clone());
env_vars.insert("ANTHROPIC_BASE_URL".to_string(), config.url.clone());
if let Some(model) = &config.model
&& !model.is_empty()
{
env_vars.insert("ANTHROPIC_MODEL".to_string(), model.clone());
}
if let Some(small_fast_model) = &config.small_fast_model
&& !small_fast_model.is_empty()
{
env_vars.insert(
"ANTHROPIC_SMALL_FAST_MODEL".to_string(),
small_fast_model.clone(),
);
}
if let Some(max_thinking_tokens) = config.max_thinking_tokens {
env_vars.insert(
"ANTHROPIC_MAX_THINKING_TOKENS".to_string(),
max_thinking_tokens.to_string(),
);
}
if let Some(timeout) = config.api_timeout_ms {
env_vars.insert("API_TIMEOUT_MS".to_string(), timeout.to_string());
}
if let Some(flag) = config.claude_code_disable_nonessential_traffic {
env_vars.insert(
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC".to_string(),
flag.to_string(),
);
}
if let Some(model) = &config.anthropic_default_sonnet_model
&& !model.is_empty()
{
env_vars.insert("ANTHROPIC_DEFAULT_SONNET_MODEL".to_string(), model.clone());
}
if let Some(model) = &config.anthropic_default_opus_model
&& !model.is_empty()
{
env_vars.insert("ANTHROPIC_DEFAULT_OPUS_MODEL".to_string(), model.clone());
}
if let Some(model) = &config.anthropic_default_haiku_model
&& !model.is_empty()
{
env_vars.insert("ANTHROPIC_DEFAULT_HAIKU_MODEL".to_string(), model.clone());
}
if let Some(model) = &config.claude_code_subagent_model
&& !model.is_empty()
{
env_vars.insert("CLAUDE_CODE_SUBAGENT_MODEL".to_string(), model.clone());
}
if let Some(flag) = config.claude_code_disable_nonstreaming_fallback {
env_vars.insert(
"CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK".to_string(),
flag.to_string(),
);
}
if let Some(level) = &config.claude_code_effort_level
&& !level.is_empty()
{
env_vars.insert("CLAUDE_CODE_EFFORT_LEVEL".to_string(), level.clone());
}
EnvironmentConfig { env_vars }
}
pub fn empty() -> Self {
EnvironmentConfig {
env_vars: EnvVarMap::new(),
}
}
pub fn as_env_tuples(&self) -> EnvVarTuples {
self.env_vars
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
}
pub fn get_config_storage_path() -> Result<PathBuf> {
let home_dir = dirs::home_dir().context("Could not find home directory")?;
Ok(home_dir.join(".claude").join("cc_auto_switch_setting.json"))
}
pub fn validate_alias_name(alias_name: &str) -> Result<()> {
if alias_name.is_empty() {
anyhow::bail!("Alias name cannot be empty");
}
if alias_name == "cc" {
anyhow::bail!("Alias name 'cc' is reserved and cannot be used");
}
if alias_name.chars().any(|c| c.is_whitespace()) {
anyhow::bail!("Alias name cannot contain whitespace");
}
Ok(())
}