use cordance_core::source::SourceClass;
#[must_use]
pub fn classify(rel_path: &str) -> SourceClass {
let p = rel_path.replace('\\', "/");
if p.starts_with("doctrine/principles/") || p.contains("/doctrine/principles/") {
return SourceClass::EngineeringDoctrinePrinciple;
}
if p.starts_with("doctrine/patterns/") || p.contains("/doctrine/patterns/") {
return SourceClass::EngineeringDoctrinePattern;
}
if p.starts_with("doctrine/checklists/") || p.contains("/doctrine/checklists/") {
return SourceClass::EngineeringDoctrineChecklist;
}
if p.starts_with("doctrine/tooling/") || p.contains("/doctrine/tooling/") {
return SourceClass::EngineeringDoctrineTooling;
}
if p.ends_with("doctrine/glossary.md") {
return SourceClass::EngineeringDoctrineGlossary;
}
if p.starts_with("doctrine/evolution/") {
return SourceClass::EngineeringDoctrineEvolution;
}
if p.starts_with("docs/adr/")
&& std::path::Path::new(&p)
.extension()
.is_some_and(|e| e.eq_ignore_ascii_case("md"))
{
return SourceClass::ProjectAdr;
}
if p.starts_with("contracts/") && p.ends_with(".schema.json") {
return SourceClass::ProjectSchema;
}
if p.starts_with("contracts/") {
return SourceClass::ProjectContract;
}
if p.starts_with("tests/") || p.contains("/tests/") {
return SourceClass::ProjectTest;
}
if p.starts_with(".github/workflows/")
|| p.starts_with(".pipelines/")
|| p == "deny.toml"
|| p == "rust-toolchain.toml"
|| p == "release-plz.toml"
|| p.ends_with(".taudit-suppressions.yml")
|| p.ends_with(".tsafe.yml")
|| p.ends_with(".gitleaksignore")
|| p.ends_with(".checkov.yaml")
{
return SourceClass::ProjectReleaseGate;
}
if p == "AGENTS.md"
|| p == "CLAUDE.md"
|| p == "CLAUDE.md.template"
|| p.starts_with("agents/")
|| p.starts_with(".cursor/")
|| p == ".claude/settings.json"
{
return SourceClass::ProjectAgentFile;
}
if p == "README.md"
|| p == "ENGINEERING.md"
|| p == "GOVERNANCE.md"
|| p == "PLAN.md"
|| p == "HORIZON.md"
|| p == "LAYERS.md"
|| p == "EXTENSIBILITY.md"
|| p == "IMPLEMENTATION_PLAN.md"
|| p == "CHANGELOG.md"
|| p == "CONTRIBUTING.md"
|| p == "SECURITY.md"
{
return SourceClass::ProjectReadme;
}
let ext_matches = std::path::Path::new(&p).extension().is_some_and(|e| {
e.eq_ignore_ascii_case("rs")
|| e.eq_ignore_ascii_case("ts")
|| e.eq_ignore_ascii_case("py")
|| e.eq_ignore_ascii_case("go")
});
if p.starts_with("crates/") || p.starts_with("src/") || ext_matches {
return SourceClass::ProjectSourceCode;
}
SourceClass::Unclassified
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn engineering_doctrine_principle_classified() {
assert_eq!(
classify("doctrine/principles/build.md"),
SourceClass::EngineeringDoctrinePrinciple
);
}
#[test]
fn adr_classified() {
assert_eq!(classify("docs/adr/0001-foo.md"), SourceClass::ProjectAdr);
}
#[test]
fn release_gate_classified() {
assert_eq!(classify("deny.toml"), SourceClass::ProjectReleaseGate);
assert_eq!(
classify(".github/workflows/quality.yml"),
SourceClass::ProjectReleaseGate
);
}
#[test]
fn windows_paths_normalised() {
assert_eq!(classify("docs\\adr\\0001-foo.md"), SourceClass::ProjectAdr);
}
}