sniff-cli 0.1.2

An exhaustive LLM-backed slop finder for codebases
Documentation
use crate::config::ResolvedConfig;
use crate::env_value;
use std::fs;
use std::path::{Path, PathBuf};

fn find_config_path(cwd: &Path) -> Option<PathBuf> {
    let mut current_dir = cwd.to_path_buf();
    loop {
        let potential = current_dir.join("sniff.config.toml");
        if potential.is_file() {
            return Some(potential);
        }
        if !current_dir.pop() {
            break;
        }
    }
    None
}

pub fn resolve_config(cwd: &Path) -> Result<ResolvedConfig, String> {
    let mut config = ResolvedConfig::default();

    if let Some(config_path) = find_config_path(cwd) {
        let content = fs::read_to_string(&config_path)
            .map_err(|err| format!("failed to read config {}: {}", config_path.display(), err))?;
        config = toml::from_str::<ResolvedConfig>(&content)
            .map_err(|err| format!("failed to parse config {}: {}", config_path.display(), err))?;
    }

    if let Some(model) = env_value::read("SNIFF_MODEL") {
        config.model = model;
    }

    if let Some(endpoint) = env_value::read("SNIFF_ENDPOINT") {
        config.llm.endpoint = endpoint;
    }

    Ok(config)
}

#[cfg(test)]
mod tests {
    use super::resolve_config;
    use std::fs;
    use std::time::{SystemTime, UNIX_EPOCH};

    #[test]
    fn invalid_config_fails_instead_of_using_defaults() {
        let unique = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let root = std::env::temp_dir().join(format!("sniff-invalid-config-{unique}"));
        fs::create_dir_all(&root).unwrap();
        fs::write(root.join("sniff.config.toml"), "[thresholds\ninvalid").unwrap();

        let err = resolve_config(&root).expect_err("invalid config should fail explicitly");
        assert!(err.contains("failed to parse config"), "{err}");

        let _ = fs::remove_dir_all(&root);
    }
}