use std::path::Path;
use crate::domain::PathClass;
use crate::languages::{LanguageCapability, supported_language_for_path};
pub(crate) fn classify_repository_path(relative_path: &str) -> PathClass {
let normalized = relative_path.trim_start_matches("./");
let components = normalized
.split('/')
.filter(|component| !component.is_empty())
.collect::<Vec<_>>();
let src_index = components.iter().position(|component| *component == "src");
let support_component_index = components
.iter()
.position(|component| is_generic_support_component(component));
let has_non_first_party_boundary = components
.iter()
.any(|component| is_non_first_party_boundary_component(component));
if support_component_index
.is_some_and(|support_index| src_index.is_none_or(|src_index| support_index < src_index))
|| has_non_first_party_boundary
{
PathClass::Support
} else if is_roc_platform_source_path(normalized)
|| normalized == "bootstrap/app.php"
|| normalized.starts_with("app/")
|| normalized.starts_with("bootstrap/")
|| normalized.starts_with("routes/")
|| normalized.starts_with("database/migrations/")
|| normalized.starts_with("database/seeders/")
|| normalized.starts_with("database/factories/")
{
PathClass::Runtime
} else if normalized.starts_with("resources/views/") {
PathClass::Support
} else if src_index.is_some() || is_repo_root_supported_source_file(normalized) {
PathClass::Runtime
} else {
PathClass::Project
}
}
fn is_generic_support_component(component: &str) -> bool {
matches!(
component,
"benches" | "bench" | "examples" | "example" | "tests" | "test"
)
}
fn is_non_first_party_boundary_component(component: &str) -> bool {
matches!(
component,
"vendor" | "node_modules" | "generated" | "dist" | "build" | "out"
)
}
fn is_repo_root_supported_source_file(relative_path: &str) -> bool {
!relative_path.contains('/')
&& supported_language_for_path(Path::new(relative_path), LanguageCapability::SymbolCorpus)
.is_some()
}
fn is_roc_platform_source_path(relative_path: &str) -> bool {
relative_path.ends_with(".roc")
&& (relative_path.starts_with("platform/") || relative_path.contains("/platform/"))
}
pub(crate) fn repository_path_class(relative_path: &str) -> &'static str {
classify_repository_path(relative_path).as_str()
}
pub(crate) fn repository_path_class_rank(path_class: &str) -> u8 {
PathClass::from_label(path_class)
.map(PathClass::rank)
.unwrap_or(3)
}
#[cfg(test)]
mod tests {
use crate::domain::PathClass;
use super::{classify_repository_path, repository_path_class};
#[test]
fn repository_path_class_treats_laravel_runtime_surfaces_as_runtime() {
assert_eq!(
repository_path_class("app/Livewire/Dashboard.php"),
"runtime"
);
assert_eq!(repository_path_class("bootstrap/app.php"), "runtime");
assert_eq!(repository_path_class("routes/web.php"), "runtime");
assert_eq!(
repository_path_class("database/migrations/2014_10_12_000000_create_users_table.php"),
"runtime"
);
assert_eq!(
repository_path_class("database/seeders/DatabaseSeeder.php"),
"runtime"
);
assert_eq!(
repository_path_class("database/factories/UserFactory.php"),
"runtime"
);
}
#[test]
fn classify_repository_path_returns_typed_classes() {
assert_eq!(
classify_repository_path("crates/cli/src/mcp/server.rs"),
PathClass::Runtime
);
assert_eq!(
classify_repository_path("crates/cli/examples/server.rs"),
PathClass::Support
);
assert_eq!(
classify_repository_path("crates/cli/src/examples/server.rs"),
PathClass::Runtime
);
assert_eq!(
classify_repository_path("src/examples/live_handler.rs"),
PathClass::Runtime
);
assert_eq!(
classify_repository_path("tests/fixtures/src/server.rs"),
PathClass::Support
);
assert_eq!(
classify_repository_path("vendor/laravel/framework/src/Foundation/Application.php"),
PathClass::Support
);
assert_eq!(
classify_repository_path("node_modules/pkg/src/index.ts"),
PathClass::Support
);
assert_eq!(
classify_repository_path("generated/client/src/api.rs"),
PathClass::Support
);
assert_eq!(classify_repository_path("Cargo.toml"), PathClass::Project);
}
#[test]
fn repository_path_class_treats_laravel_views_and_tests_as_support() {
assert_eq!(
repository_path_class("resources/views/auth/login.blade.php"),
"support"
);
assert_eq!(repository_path_class("tests/DuskTestCase.php"), "support");
}
#[test]
fn repository_path_class_treats_repo_root_supported_source_files_as_runtime() {
assert_eq!(repository_path_class("main.py"), "runtime");
assert_eq!(repository_path_class("index.tsx"), "runtime");
assert_eq!(repository_path_class("main.go"), "runtime");
assert_eq!(repository_path_class("Main.kt"), "runtime");
assert_eq!(repository_path_class("init.lua"), "runtime");
assert_eq!(repository_path_class("Main.roc"), "runtime");
assert_eq!(repository_path_class("config.nims"), "runtime");
}
#[test]
fn repository_path_class_treats_roc_platform_surfaces_as_runtime() {
assert_eq!(repository_path_class("platform/main.roc"), "runtime");
assert_eq!(repository_path_class("platform/Arg.roc"), "runtime");
}
}