Skip to main content

cc_switch_lib/cli/commands/
env.rs

1use crate::app_config::AppType;
2use crate::cli::ui::{create_table, error, highlight, info, success};
3use crate::error::AppError;
4use crate::services::env_checker;
5use crate::services::local_env_check::{check_local_environment, ToolCheckStatus};
6use clap::Subcommand;
7
8#[derive(Subcommand)]
9pub enum EnvCommand {
10    /// Check for environment variable conflicts
11    Check,
12    /// List all relevant environment variables
13    List,
14    /// Check whether Claude/Codex/Gemini/OpenCode CLIs are installed locally
15    Tools,
16}
17
18pub fn execute(cmd: EnvCommand, app: Option<AppType>) -> Result<(), AppError> {
19    let app_type = app.unwrap_or(AppType::Claude);
20
21    match cmd {
22        EnvCommand::Check => check_conflicts(app_type),
23        EnvCommand::List => list_env_vars(app_type),
24        EnvCommand::Tools => check_local_tools(),
25    }
26}
27
28fn check_conflicts(app_type: AppType) -> Result<(), AppError> {
29    let app_str = app_type.as_str();
30
31    println!(
32        "\n{}",
33        highlight(&format!("Checking Environment Variables for {}", app_str))
34    );
35    println!("{}", "═".repeat(60));
36
37    // 检测冲突
38    let conflicts = env_checker::check_env_conflicts(app_str)
39        .map_err(|e| AppError::Message(format!("Failed to check environment variables: {}", e)))?;
40
41    if conflicts.is_empty() {
42        println!(
43            "\n{}",
44            success("✓ No environment variable conflicts detected")
45        );
46        println!(
47            "{}",
48            info(&format!(
49                "Your {} configuration should work correctly.",
50                app_str
51            ))
52        );
53        return Ok(());
54    }
55
56    // 显示冲突
57    println!(
58        "\n{}",
59        error(&format!(
60            "⚠ Found {} environment variable(s) that may conflict:",
61            conflicts.len()
62        ))
63    );
64    println!();
65
66    let mut table = create_table();
67    table.set_header(vec!["Variable", "Value", "Source Type", "Source Location"]);
68
69    for conflict in &conflicts {
70        // 截断过长的值
71        let value_display = if conflict.var_value.len() > 30 {
72            format!("{}...", &conflict.var_value[..27])
73        } else {
74            conflict.var_value.clone()
75        };
76
77        table.add_row(vec![
78            conflict.var_name.as_str(),
79            &value_display,
80            conflict.source_type.as_str(),
81            conflict.source_path.as_str(),
82        ]);
83    }
84
85    println!("{}", table);
86    println!();
87    println!(
88        "{}",
89        info("These environment variables may override CC-Switch's configuration.")
90    );
91    println!(
92        "{}",
93        info("Please manually remove them from your shell config files or system settings.")
94    );
95
96    Ok(())
97}
98
99fn list_env_vars(app_type: AppType) -> Result<(), AppError> {
100    let app_str = app_type.as_str();
101
102    println!(
103        "\n{}",
104        highlight(&format!("Environment Variables for {}", app_str))
105    );
106    println!("{}", "═".repeat(60));
107
108    // 获取所有相关环境变量
109    let conflicts = env_checker::check_env_conflicts(app_str)
110        .map_err(|e| AppError::Message(format!("Failed to list environment variables: {}", e)))?;
111
112    if conflicts.is_empty() {
113        println!("\n{}", info("No related environment variables found."));
114        return Ok(());
115    }
116
117    println!("\n{} environment variable(s) found:\n", conflicts.len());
118
119    let mut table = create_table();
120    table.set_header(vec!["Variable", "Value", "Source Type", "Source Location"]);
121
122    for conflict in &conflicts {
123        table.add_row(vec![
124            conflict.var_name.as_str(),
125            conflict.var_value.as_str(),
126            conflict.source_type.as_str(),
127            conflict.source_path.as_str(),
128        ]);
129    }
130
131    println!("{}", table);
132
133    Ok(())
134}
135
136fn check_local_tools() -> Result<(), AppError> {
137    let results = check_local_environment();
138
139    println!("\n{}", highlight("Local CLI Tools"));
140    println!("{}", "═".repeat(60));
141
142    let mut table = create_table();
143    table.set_header(vec!["Tool", "Status"]);
144    for result in results {
145        table.add_row(vec![
146            result.display_name.to_string(),
147            tool_status_summary(&result.status),
148        ]);
149    }
150
151    println!("{}", table);
152
153    Ok(())
154}
155
156fn tool_status_summary(status: &ToolCheckStatus) -> String {
157    match status {
158        ToolCheckStatus::Ok { version } => format!("ok ({version})"),
159        ToolCheckStatus::NotInstalledOrNotExecutable => "not installed".to_string(),
160        ToolCheckStatus::Error { message } => format!("error ({message})"),
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::tool_status_summary;
167    use crate::services::local_env_check::ToolCheckStatus;
168
169    #[test]
170    fn tool_status_summary_formats_ok_version() {
171        let summary = tool_status_summary(&ToolCheckStatus::Ok {
172            version: "1.2.3".to_string(),
173        });
174
175        assert_eq!(summary, "ok (1.2.3)");
176    }
177
178    #[test]
179    fn tool_status_summary_formats_missing_tool() {
180        let summary = tool_status_summary(&ToolCheckStatus::NotInstalledOrNotExecutable);
181
182        assert_eq!(summary, "not installed");
183    }
184}