codesearch 0.1.15

A fast, intelligent CLI tool with multiple search modes (regex, fuzzy, semantic), code analysis, and dead code detection for popular programming languages
Documentation
//! Configuration file support for codesearch
//!
//! Reads `.codesearch.toml` from the search path to provide default options.
//! CLI arguments always override config file values.

use serde::Deserialize;
use std::path::Path;

/// Config file format for codesearch defaults.
#[derive(Debug, Deserialize, Default)]
pub struct Config {
    /// Default file extensions to search
    pub extensions: Option<Vec<String>>,
    /// Default directories to exclude
    pub exclude: Option<Vec<String>>,
    /// Default maximum results per file
    pub max_results: Option<usize>,
    /// Default case-insensitive search
    pub ignore_case: Option<bool>,
    /// Default fuzzy search
    pub fuzzy: Option<bool>,
    /// Default fuzzy threshold
    pub fuzzy_threshold: Option<f64>,
    /// Default ranking mode
    pub rank: Option<bool>,
}

impl Config {
    /// Load `.codesearch.toml` from the given path (or current directory).
    pub fn load(path: &Path) -> Option<Self> {
        let config_path = path.join(".codesearch.toml");
        if !config_path.exists() {
            // Fall back to current directory
            let cwd_config = std::env::current_dir().ok()?.join(".codesearch.toml");
            if cwd_config.exists() {
                let content = std::fs::read_to_string(&cwd_config).ok()?;
                return toml::from_str(&content).ok();
            }
            return None;
        }
        let content = std::fs::read_to_string(&config_path).ok()?;
        toml::from_str(&content).ok()
    }
}

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

    #[test]
    fn test_config_parsing() {
        let dir = tempfile::tempdir().unwrap();
        let config_path = dir.path().join(".codesearch.toml");
        let mut file = std::fs::File::create(&config_path).unwrap();
        file.write_all(
            br#"extensions = ["rs", "py"]
exclude = ["target", "node_modules"]
max_results = 50
ignore_case = true
fuzzy = true
fuzzy_threshold = 0.8
rank = true
"#,
        )
        .unwrap();

        let config = Config::load(dir.path()).expect("Failed to load config");
        assert_eq!(
            config.extensions,
            Some(vec!["rs".to_string(), "py".to_string()])
        );
        assert_eq!(
            config.exclude,
            Some(vec!["target".to_string(), "node_modules".to_string()])
        );
        assert_eq!(config.max_results, Some(50));
        assert_eq!(config.ignore_case, Some(true));
        assert_eq!(config.fuzzy, Some(true));
        assert_eq!(config.fuzzy_threshold, Some(0.8));
        assert_eq!(config.rank, Some(true));
    }

    #[test]
    fn test_config_missing_file() {
        let dir = tempfile::tempdir().unwrap();
        assert!(Config::load(dir.path()).is_none());
    }
}