crabmate 0.5.0

Rust AI agent: OpenAI-compatible chat/completions, function calling, HTTP serve, ops CLI
Documentation
//! 环境变量批量检查工具(只读、脱敏)

fn format_one_env_var_line(
    raw_name: &str,
    sanitized: &str,
    val: Result<String, std::env::VarError>,
    show_length: bool,
    show_prefix: usize,
) -> String {
    if sanitized.is_empty() {
        return format!("  {}: (无效变量名)", raw_name);
    }
    match val {
        Ok(val) => {
            let mut info = if val.is_empty() {
                "已设置(空值)".to_string()
            } else {
                "已设置".to_string()
            };
            if !val.is_empty() {
                if show_length {
                    info.push_str(&format!(",长度={}", val.len()));
                }
                if show_prefix > 0 {
                    let prefix: String = val.chars().take(show_prefix).collect();
                    info.push_str(&format!(",前缀={:?}", prefix));
                }
            }
            format!("  {}: {}", sanitized, info)
        }
        Err(_) => format!("  {}: 未设置", sanitized),
    }
}

pub fn run(args_json: &str) -> String {
    let v = match super::parse_args_json(args_json) {
        Ok(v) => v,
        Err(e) => return e,
    };
    if v.get("names").is_none() {
        return "错误:缺少 names 数组参数".to_string();
    }
    let args: super::tool_param_types::EnvVarCheckArgs = match serde_json::from_value(v) {
        Ok(a) => a,
        Err(e) => return format!("参数 JSON 无效: {e}"),
    };
    let names: Vec<&str> = args.names.iter().map(String::as_str).collect();
    if names.is_empty() {
        return "错误:names 不能为空".to_string();
    }
    if names.len() > 50 {
        return "错误:names 上限 50 个".to_string();
    }

    let show_length = args.show_length.unwrap_or(false);
    let show_prefix = args.show_prefix_chars.unwrap_or(0).min(8) as usize;

    let mut lines: Vec<String> = Vec::new();
    let mut set_count = 0usize;
    for name in &names {
        let sanitized = name
            .trim()
            .replace(|c: char| !c.is_ascii_alphanumeric() && c != '_', "");
        let val = std::env::var(&sanitized);
        if val.is_ok() {
            set_count += 1;
        }
        lines.push(format_one_env_var_line(
            name,
            &sanitized,
            val,
            show_length,
            show_prefix,
        ));
    }
    format!(
        "环境变量检查({}/{}已设置):\n{}",
        set_count,
        names.len(),
        lines.join("\n")
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn check_path_is_set() {
        let out = run(r#"{"names":["PATH"]}"#);
        assert!(out.contains("已设置"), "PATH should be set, got: {}", out);
        assert!(out.contains("1/1"));
    }

    #[test]
    fn check_nonexistent_var() {
        let out = run(r#"{"names":["CM_TEST_NONEXISTENT_VAR_XYZ"]}"#);
        assert!(out.contains("未设置"), "out={}", out);
        assert!(out.contains("0/1"));
    }

    #[test]
    fn show_length() {
        let out = run(r#"{"names":["PATH"],"show_length":true}"#);
        assert!(out.contains("长度="), "out={}", out);
    }

    #[test]
    fn empty_names() {
        let out = run(r#"{"names":[]}"#);
        assert!(out.contains("不能为空"));
    }

    #[test]
    fn missing_names() {
        let out = run(r#"{}"#);
        assert!(out.contains("缺少 names"));
    }
}