relay-knowledge 1.1.17

Graph-database-based knowledge graph project.
Documentation
//! Deterministic software-projection file role classification.

use crate::project::{
    KNOWLEDGE_MAP_RELATIVE_PATH, KNOWLEDGE_MAP_TOPICS_RELATIVE_PREFIX,
    LEGACY_KNOWLEDGE_MAP_RELATIVE_PATH,
};

pub(super) fn file_role(
    path: &str,
    language_id: &str,
    authorized_topic_shard: bool,
) -> &'static str {
    if matches!(
        path,
        KNOWLEDGE_MAP_RELATIVE_PATH | LEGACY_KNOWLEDGE_MAP_RELATIVE_PATH
    ) {
        return "knowledge_map_manifest";
    }
    if authorized_topic_shard
        && (path.starts_with(KNOWLEDGE_MAP_TOPICS_RELATIVE_PREFIX)
            || path.starts_with(".knowledge/topics/"))
    {
        return "knowledge_map_topic_shard";
    }
    let file_name = path.rsplit('/').next().unwrap_or(path);
    if language_id == "markdown" {
        return "documentation";
    }
    if api_schema_path(file_name, language_id) {
        return "api_schema";
    }
    if dependency_manifest_path(path, file_name) {
        return "dependency_manifest";
    }
    if build_manifest_path(file_name, language_id) {
        return "build_manifest";
    }
    if deployment_path(path, file_name, language_id) {
        return "deployment";
    }
    if test_path(path, file_name) {
        return "test";
    }
    if template_language(language_id) {
        return "template";
    }
    if config_language(language_id) {
        return "configuration";
    }

    "source"
}

fn api_schema_path(file_name: &str, language_id: &str) -> bool {
    if !matches!(language_id, "json" | "yaml") {
        return false;
    }
    file_name
        .to_ascii_lowercase()
        .split(['.', '-', '_'])
        .any(|segment| matches!(segment, "openapi" | "swagger"))
}

fn dependency_manifest_path(path: &str, file_name: &str) -> bool {
    matches!(
        file_name,
        "Cargo.toml"
            | "Cargo.lock"
            | "package.json"
            | "package-lock.json"
            | "go.mod"
            | "go.sum"
            | "requirements.txt"
            | "pyproject.toml"
            | "uv.lock"
            | "pom.xml"
            | "build.gradle"
            | "build.gradle.kts"
            | "gradle.lockfile"
            | "conanfile.txt"
            | "conanfile.py"
            | "CMakeLists.txt"
    ) || python_requirements_path(path, file_name)
}

fn python_requirements_path(path: &str, file_name: &str) -> bool {
    file_name.ends_with(".txt")
        && (file_name.starts_with("requirements")
            || file_name.starts_with("constraints")
            || path.split('/').any(|segment| segment == "requirements"))
}

fn build_manifest_path(file_name: &str, language_id: &str) -> bool {
    matches!(
        file_name,
        "BUILD"
            | "BUILD.bazel"
            | "WORKSPACE"
            | "WORKSPACE.bazel"
            | "MODULE.bazel"
            | "Makefile"
            | "GNUmakefile"
            | "BSDmakefile"
            | "CMakeLists.txt"
            | "build.ninja"
    ) || file_name.starts_with("Dockerfile")
        || file_name.starts_with("Containerfile")
        || matches!(
            language_id,
            "cmake" | "make" | "ninja" | "starlark" | "dockerfile"
        )
}

fn deployment_path(path: &str, file_name: &str, language_id: &str) -> bool {
    (deployment_service_path(path)
        && (deployment_manifest_language(language_id) || service_manager_file_name(file_name)))
        || (kubernetes_manifest_path(path) && deployment_manifest_language(language_id))
}

fn deployment_service_path(path: &str) -> bool {
    path.starts_with("systemd/")
        || path.starts_with("launchd/")
        || path.contains("/systemd/")
        || path.contains("/launchd/")
}

fn kubernetes_manifest_path(path: &str) -> bool {
    path.starts_with("k8s/")
        || path.starts_with("kubernetes/")
        || path.contains("/k8s/")
        || path.contains("/kubernetes/")
}

fn deployment_manifest_language(language_id: &str) -> bool {
    config_language(language_id) || template_language(language_id)
}

fn service_manager_file_name(file_name: &str) -> bool {
    file_name.ends_with(".service")
        || file_name.ends_with(".socket")
        || file_name.ends_with(".timer")
        || file_name.ends_with(".target")
        || file_name.ends_with(".plist")
}

fn test_path(path: &str, file_name: &str) -> bool {
    path.starts_with("test/")
        || path.starts_with("tests/")
        || path.contains("/test/")
        || path.contains("/tests/")
        || file_name.contains("_test.")
        || file_name.contains(".test.")
        || file_name.contains(".spec.")
}

fn template_language(language_id: &str) -> bool {
    matches!(language_id, "jinja2" | "gotemplate")
}

fn config_language(language_id: &str) -> bool {
    matches!(
        language_id,
        "json" | "toml" | "yaml" | "ini" | "properties" | "xml" | "jinja2" | "gotemplate"
    )
}

#[cfg(test)]
#[path = "file_role_tests.rs"]
mod tests;