agent-source-repository 0.1.0

Agent Source Repository local context registry for coding agents
Documentation
use std::collections::HashSet;
use std::path::Path;

use once_cell::sync::Lazy;
use tree_sitter::Language;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FileCategory {
    Code,
    Document,
}

#[derive(Debug, Clone)]
pub struct FileType {
    pub language: &'static str,
    pub category: FileCategory,
}

static FILE_TYPES: Lazy<Vec<(&'static str, FileType)>> = Lazy::new(|| {
    vec![
        (
            ".py",
            FileType {
                language: "python",
                category: FileCategory::Code,
            },
        ),
        (
            ".js",
            FileType {
                language: "javascript",
                category: FileCategory::Code,
            },
        ),
        (
            ".jsx",
            FileType {
                language: "jsx",
                category: FileCategory::Code,
            },
        ),
        (
            ".ts",
            FileType {
                language: "typescript",
                category: FileCategory::Code,
            },
        ),
        (
            ".tsx",
            FileType {
                language: "tsx",
                category: FileCategory::Code,
            },
        ),
        (
            ".go",
            FileType {
                language: "go",
                category: FileCategory::Code,
            },
        ),
        (
            ".rs",
            FileType {
                language: "rust",
                category: FileCategory::Code,
            },
        ),
        (
            ".java",
            FileType {
                language: "java",
                category: FileCategory::Code,
            },
        ),
        (
            ".kt",
            FileType {
                language: "kotlin",
                category: FileCategory::Code,
            },
        ),
        (
            ".kts",
            FileType {
                language: "kotlin",
                category: FileCategory::Code,
            },
        ),
        (
            ".rb",
            FileType {
                language: "ruby",
                category: FileCategory::Code,
            },
        ),
        (
            ".php",
            FileType {
                language: "php",
                category: FileCategory::Code,
            },
        ),
        (
            ".c",
            FileType {
                language: "c",
                category: FileCategory::Code,
            },
        ),
        (
            ".h",
            FileType {
                language: "c",
                category: FileCategory::Code,
            },
        ),
        (
            ".cpp",
            FileType {
                language: "cpp",
                category: FileCategory::Code,
            },
        ),
        (
            ".hpp",
            FileType {
                language: "cpp",
                category: FileCategory::Code,
            },
        ),
        (
            ".cs",
            FileType {
                language: "csharp",
                category: FileCategory::Code,
            },
        ),
        (
            ".swift",
            FileType {
                language: "swift",
                category: FileCategory::Code,
            },
        ),
        (
            ".scala",
            FileType {
                language: "scala",
                category: FileCategory::Code,
            },
        ),
        (
            ".sbt",
            FileType {
                language: "scala",
                category: FileCategory::Code,
            },
        ),
        (
            ".ex",
            FileType {
                language: "elixir",
                category: FileCategory::Code,
            },
        ),
        (
            ".exs",
            FileType {
                language: "elixir",
                category: FileCategory::Code,
            },
        ),
        (
            ".dart",
            FileType {
                language: "dart",
                category: FileCategory::Code,
            },
        ),
        (
            ".lua",
            FileType {
                language: "lua",
                category: FileCategory::Code,
            },
        ),
        (
            ".sql",
            FileType {
                language: "sql",
                category: FileCategory::Code,
            },
        ),
        (
            ".sh",
            FileType {
                language: "bash",
                category: FileCategory::Code,
            },
        ),
        (
            ".bash",
            FileType {
                language: "bash",
                category: FileCategory::Code,
            },
        ),
        (
            ".zig",
            FileType {
                language: "zig",
                category: FileCategory::Code,
            },
        ),
        (
            ".hs",
            FileType {
                language: "haskell",
                category: FileCategory::Code,
            },
        ),
        (
            ".md",
            FileType {
                language: "markdown",
                category: FileCategory::Document,
            },
        ),
        (
            ".yaml",
            FileType {
                language: "yaml",
                category: FileCategory::Document,
            },
        ),
        (
            ".yml",
            FileType {
                language: "yaml",
                category: FileCategory::Document,
            },
        ),
        (
            ".toml",
            FileType {
                language: "toml",
                category: FileCategory::Document,
            },
        ),
        (
            ".json",
            FileType {
                language: "json",
                category: FileCategory::Document,
            },
        ),
    ]
});

pub fn file_types() -> &'static [(&'static str, FileType)] {
    &FILE_TYPES
}

pub fn language_for_path(path: &Path) -> Option<&'static str> {
    let ext = path.extension()?.to_str()?;
    let dot_ext = format!(".{}", ext.to_lowercase());
    file_types()
        .iter()
        .find(|(e, _)| *e == dot_ext)
        .map(|(_, ft)| ft.language)
}

pub fn filter_extensions(
    extensions: Option<&HashSet<String>>,
    include_text_files: bool,
) -> HashSet<String> {
    if let Some(ext) = extensions {
        return ext.clone();
    }
    let mut categories = HashSet::new();
    categories.insert(FileCategory::Code);
    if include_text_files {
        categories.insert(FileCategory::Document);
    }
    file_types()
        .iter()
        .filter(|(_, ft)| categories.contains(&ft.category))
        .map(|(ext, _)| ext.to_string())
        .collect()
}

pub(crate) fn tree_sitter_language(name: &str) -> Option<Language> {
    let lang_fn = match name {
        "rust" => tree_sitter_rust::LANGUAGE,
        "python" => tree_sitter_python::LANGUAGE,
        "javascript" | "jsx" => tree_sitter_javascript::LANGUAGE,
        "typescript" => tree_sitter_typescript::LANGUAGE_TYPESCRIPT,
        "tsx" => tree_sitter_typescript::LANGUAGE_TSX,
        "go" => tree_sitter_go::LANGUAGE,
        "java" => tree_sitter_java::LANGUAGE,
        "c" => tree_sitter_c::LANGUAGE,
        "cpp" => tree_sitter_cpp::LANGUAGE,
        _ => return None,
    };
    Some(Language::from(lang_fn))
}

pub(crate) fn is_javascript_like(language: &str) -> bool {
    matches!(language, "javascript" | "jsx")
}

pub(crate) fn is_typescript_like(language: &str) -> bool {
    matches!(language, "typescript" | "tsx")
}

pub(crate) fn is_js_ts_like(language: &str) -> bool {
    is_javascript_like(language) || is_typescript_like(language)
}