use std::path::Path;
pub mod async_detector;
pub mod detector;
pub mod rules;
pub use detector::ContextDetector;
pub use rules::{ContextRule, ContextRuleEngine, RuleAction};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionContext {
pub role: FunctionRole,
pub file_type: FileType,
pub is_async: bool,
pub framework_pattern: Option<FrameworkPattern>,
pub function_name: Option<String>,
pub module_path: Vec<String>,
}
impl Default for FunctionContext {
fn default() -> Self {
Self {
role: FunctionRole::Unknown,
file_type: FileType::Production,
is_async: false,
framework_pattern: None,
function_name: None,
module_path: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FunctionRole {
Main,
ConfigLoader,
TestFunction,
Handler,
Initialization,
Utility,
BuildScript,
Example,
Debug,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum FileType {
Production,
Test,
Benchmark,
Example,
BuildScript,
Documentation,
Configuration,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameworkPattern {
RustMain,
PythonMain,
WebHandler,
CliHandler,
TestFramework,
AsyncRuntime,
ConfigInit,
}
impl FunctionContext {
pub fn new() -> Self {
Self::default()
}
pub fn with_role(mut self, role: FunctionRole) -> Self {
self.role = role;
self
}
pub fn with_file_type(mut self, file_type: FileType) -> Self {
self.file_type = file_type;
self
}
pub fn with_async(mut self, is_async: bool) -> Self {
self.is_async = is_async;
self
}
pub fn with_framework_pattern(mut self, pattern: FrameworkPattern) -> Self {
self.framework_pattern = Some(pattern);
self
}
pub fn with_function_name(mut self, name: String) -> Self {
self.function_name = Some(name);
self
}
pub fn with_module_path(mut self, path: Vec<String>) -> Self {
self.module_path = path;
self
}
pub fn is_test(&self) -> bool {
self.role == FunctionRole::TestFunction || self.file_type == FileType::Test
}
pub fn is_entry_point(&self) -> bool {
matches!(
self.role,
FunctionRole::Main | FunctionRole::Handler | FunctionRole::Initialization
) || matches!(
self.framework_pattern,
Some(
FrameworkPattern::RustMain
| FrameworkPattern::PythonMain
| FrameworkPattern::AsyncRuntime
)
)
}
pub fn allows_blocking_io(&self) -> bool {
match self.role {
FunctionRole::Main
| FunctionRole::ConfigLoader
| FunctionRole::TestFunction
| FunctionRole::Initialization
| FunctionRole::BuildScript => true,
_ => !self.is_async,
}
}
pub fn skip_security_checks(&self) -> bool {
matches!(
self.file_type,
FileType::Test | FileType::Example | FileType::Documentation
)
}
pub fn severity_adjustment(&self) -> i32 {
match (self.role, self.file_type) {
(FunctionRole::TestFunction, _) | (_, FileType::Test) => -2,
(_, FileType::Example | FileType::Documentation) => -2,
(FunctionRole::Main | FunctionRole::Handler, _) => 1,
_ => 0,
}
}
}
pub fn detect_file_type(path: &Path) -> FileType {
let path_str = path.to_string_lossy();
match () {
_ if is_test_file(&path_str) => FileType::Test,
_ if is_benchmark_file(&path_str) => FileType::Benchmark,
_ if is_example_file(&path_str) => FileType::Example,
_ if path_str.ends_with("build.rs") => FileType::BuildScript,
_ if is_documentation_file(&path_str) => FileType::Documentation,
_ if is_configuration_file(&path_str) => FileType::Configuration,
_ => FileType::Production,
}
}
fn is_test_file(path: &str) -> bool {
const TEST_PATTERNS_DIR: &[&str] = &["tests/", "tests\\"];
const TEST_PATTERNS_FILE: &[&str] = &[
"_test.rs",
"_tests.rs",
"test.py",
"_test.py",
".test.js",
".test.ts",
".spec.js",
".spec.ts",
];
TEST_PATTERNS_DIR
.iter()
.any(|pattern| path.contains(pattern))
|| TEST_PATTERNS_FILE
.iter()
.any(|pattern| path.ends_with(pattern))
}
fn is_benchmark_file(path: &str) -> bool {
const BENCHMARK_PATTERNS_DIR: &[&str] =
&["benches/", "benches\\", "benchmarks/", "benchmarks\\"];
const BENCHMARK_PATTERNS_FILE: &[&str] = &["_bench.rs", "_benchmark.rs"];
BENCHMARK_PATTERNS_DIR
.iter()
.any(|pattern| path.contains(pattern))
|| BENCHMARK_PATTERNS_FILE
.iter()
.any(|pattern| path.ends_with(pattern))
}
fn is_example_file(path: &str) -> bool {
const EXAMPLE_PATTERNS_DIR: &[&str] = &["examples/", "examples\\"];
const EXAMPLE_PATTERNS_FILE: &[&str] = &["_example.rs", "example.py"];
EXAMPLE_PATTERNS_DIR
.iter()
.any(|pattern| path.contains(pattern))
|| EXAMPLE_PATTERNS_FILE
.iter()
.any(|pattern| path.ends_with(pattern))
}
fn is_documentation_file(path: &str) -> bool {
path.ends_with(".md") || path.ends_with(".rst")
}
fn is_configuration_file(path: &str) -> bool {
const CONFIG_EXTENSIONS: &[&str] = &[".toml", ".yaml", ".yml", ".json", ".ini", ".cfg"];
CONFIG_EXTENSIONS.iter().any(|ext| path.ends_with(ext))
}
pub fn detect_function_role(name: &str, is_test_attr: bool) -> FunctionRole {
match () {
_ if is_test_attr || is_test_function_name(name) => FunctionRole::TestFunction,
_ if matches!(name, "main" | "__main__" | "Main") => FunctionRole::Main,
_ if is_config_function(name) => FunctionRole::ConfigLoader,
_ if is_initialization_function(name) => FunctionRole::Initialization,
_ if is_handler_function(name) => FunctionRole::Handler,
_ if is_utility_function(name) => FunctionRole::Utility,
_ => FunctionRole::Unknown,
}
}
fn is_test_function_name(name: &str) -> bool {
name.starts_with("test_")
|| name.ends_with("_test")
|| name.starts_with("it_")
|| name.starts_with("should_")
}
fn is_config_function(name: &str) -> bool {
name.contains("load_config")
|| name.contains("read_config")
|| name.contains("parse_config")
|| name.contains("init_config")
|| matches!(name, "configure" | "setup_configuration")
}
fn is_initialization_function(name: &str) -> bool {
name.starts_with("init_")
|| name.starts_with("setup_")
|| name.starts_with("initialize_")
|| matches!(name, "init" | "setup" | "initialize")
}
fn is_handler_function(name: &str) -> bool {
name.contains("handle_")
|| name.ends_with("_handler")
|| name == "handler"
|| name.starts_with("on_")
|| name.starts_with("process_")
}
fn is_utility_function(name: &str) -> bool {
name.starts_with("helper_")
|| name.starts_with("util_")
|| name.contains("_helper")
|| name.contains("_util")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_type_detection() {
assert_eq!(
detect_file_type(Path::new("/src/tests/foo.rs")),
FileType::Test
);
assert_eq!(
detect_file_type(Path::new("/src/foo_test.rs")),
FileType::Test
);
assert_eq!(
detect_file_type(Path::new("/src/foo.test.js")),
FileType::Test
);
assert_eq!(
detect_file_type(Path::new("/benches/bench.rs")),
FileType::Benchmark
);
assert_eq!(
detect_file_type(Path::new("/examples/demo.rs")),
FileType::Example
);
assert_eq!(
detect_file_type(Path::new("build.rs")),
FileType::BuildScript
);
assert_eq!(
detect_file_type(Path::new("README.md")),
FileType::Documentation
);
assert_eq!(
detect_file_type(Path::new("config.toml")),
FileType::Configuration
);
assert_eq!(
detect_file_type(Path::new("/src/main.rs")),
FileType::Production
);
}
#[test]
fn test_function_role_detection() {
assert_eq!(
detect_function_role("test_something", false),
FunctionRole::TestFunction
);
assert_eq!(
detect_function_role("something_test", false),
FunctionRole::TestFunction
);
assert_eq!(detect_function_role("main", false), FunctionRole::Main);
assert_eq!(
detect_function_role("load_config", false),
FunctionRole::ConfigLoader
);
assert_eq!(
detect_function_role("init_database", false),
FunctionRole::Initialization
);
assert_eq!(
detect_function_role("handle_request", false),
FunctionRole::Handler
);
assert_eq!(
detect_function_role("helper_function", false),
FunctionRole::Utility
);
assert_eq!(
detect_function_role("some_function", false),
FunctionRole::Unknown
);
}
#[test]
fn test_is_test_function_name() {
assert!(is_test_function_name("test_addition"));
assert!(is_test_function_name("complex_test"));
assert!(is_test_function_name("it_should_work"));
assert!(is_test_function_name("should_process_data"));
assert!(!is_test_function_name("testing_helper"));
assert!(!is_test_function_name("get_test_data"));
}
#[test]
fn test_is_config_function() {
assert!(is_config_function("load_config"));
assert!(is_config_function("read_config_file"));
assert!(is_config_function("parse_config"));
assert!(is_config_function("init_config"));
assert!(is_config_function("configure"));
assert!(is_config_function("setup_configuration"));
assert!(!is_config_function("config_value"));
assert!(!is_config_function("get_configuration"));
}
#[test]
fn test_is_initialization_function() {
assert!(is_initialization_function("init_system"));
assert!(is_initialization_function("setup_database"));
assert!(is_initialization_function("initialize_cache"));
assert!(is_initialization_function("init"));
assert!(is_initialization_function("setup"));
assert!(is_initialization_function("initialize"));
assert!(!is_initialization_function("initial_value"));
assert!(!is_initialization_function("get_setup"));
}
#[test]
fn test_is_handler_function() {
assert!(is_handler_function("handle_request"));
assert!(is_handler_function("request_handler"));
assert!(is_handler_function("api_handler"));
assert!(is_handler_function("on_message"));
assert!(is_handler_function("process_event"));
assert!(!is_handler_function("handler_config"));
assert!(!is_handler_function("processing_time"));
}
#[test]
fn test_is_utility_function() {
assert!(is_utility_function("helper_parse"));
assert!(is_utility_function("util_format"));
assert!(is_utility_function("string_helper"));
assert!(is_utility_function("date_util"));
assert!(!is_utility_function("helpful_message"));
assert!(!is_utility_function("utility_bill"));
}
#[test]
fn test_context_methods() {
let test_context = FunctionContext::new()
.with_role(FunctionRole::TestFunction)
.with_file_type(FileType::Test);
assert!(test_context.is_test());
assert!(test_context.allows_blocking_io());
assert!(test_context.skip_security_checks());
assert_eq!(test_context.severity_adjustment(), -2);
let main_context = FunctionContext::new()
.with_role(FunctionRole::Main)
.with_framework_pattern(FrameworkPattern::RustMain);
assert!(main_context.is_entry_point());
assert!(main_context.allows_blocking_io());
assert!(!main_context.skip_security_checks());
assert_eq!(main_context.severity_adjustment(), 1);
let async_handler = FunctionContext::new()
.with_role(FunctionRole::Handler)
.with_async(true);
assert!(async_handler.is_entry_point());
assert!(!async_handler.allows_blocking_io());
}
#[test]
fn test_is_test_file() {
use super::is_test_file;
assert!(is_test_file("tests/module.rs"));
assert!(is_test_file("/src/tests/module.rs"));
assert!(is_test_file("C:\\project\\tests\\file.rs"));
assert!(is_test_file("mod_test.rs"));
assert!(is_test_file("mod_tests.rs"));
assert!(is_test_file("test.py"));
assert!(is_test_file("module_test.py"));
assert!(is_test_file("component.test.js"));
assert!(is_test_file("component.test.ts"));
assert!(is_test_file("component.spec.js"));
assert!(is_test_file("component.spec.ts"));
assert!(!is_test_file("src/main.rs"));
assert!(!is_test_file("lib.rs"));
assert!(!is_test_file("testing_utils.rs"));
}
#[test]
fn test_is_benchmark_file() {
use super::is_benchmark_file;
assert!(is_benchmark_file("benches/perf.rs"));
assert!(is_benchmark_file("/src/benches/perf.rs"));
assert!(is_benchmark_file("C:\\project\\benches\\perf.rs"));
assert!(is_benchmark_file("benchmarks/perf.rs"));
assert!(is_benchmark_file("/src/benchmarks/perf.rs"));
assert!(is_benchmark_file("C:\\project\\benchmarks\\perf.rs"));
assert!(is_benchmark_file("perf_bench.rs"));
assert!(is_benchmark_file("perf_benchmark.rs"));
assert!(!is_benchmark_file("bench.rs"));
assert!(!is_benchmark_file("src/main.rs"));
assert!(!is_benchmark_file("benches.toml"));
}
#[test]
fn test_is_example_file() {
use super::is_example_file;
assert!(is_example_file("examples/demo.rs"));
assert!(is_example_file("/src/examples/demo.rs"));
assert!(is_example_file("C:\\project\\examples\\demo.rs"));
assert!(is_example_file("demo_example.rs"));
assert!(is_example_file("example.py"));
assert!(!is_example_file("examples.rs"));
assert!(!is_example_file("src/main.rs"));
assert!(!is_example_file("example.txt"));
}
#[test]
fn test_is_documentation_file() {
use super::is_documentation_file;
assert!(is_documentation_file("README.md"));
assert!(is_documentation_file("docs/guide.md"));
assert!(is_documentation_file("api.rst"));
assert!(is_documentation_file("docs/tutorial.rst"));
assert!(!is_documentation_file("main.rs"));
assert!(!is_documentation_file("config.toml"));
}
#[test]
fn test_is_configuration_file() {
use super::is_configuration_file;
assert!(is_configuration_file("Cargo.toml"));
assert!(is_configuration_file("config.yaml"));
assert!(is_configuration_file("settings.yml"));
assert!(is_configuration_file("package.json"));
assert!(is_configuration_file("setup.ini"));
assert!(is_configuration_file("app.cfg"));
assert!(!is_configuration_file("main.rs"));
assert!(!is_configuration_file("README.md"));
assert!(!is_configuration_file("config.rs"));
}
#[test]
fn test_detect_file_type_comprehensive() {
use std::path::Path;
assert_eq!(
detect_file_type(Path::new("tests/integration.rs")),
FileType::Test
);
assert_eq!(
detect_file_type(Path::new("module_test.rs")),
FileType::Test
);
assert_eq!(detect_file_type(Path::new("app.test.js")), FileType::Test);
assert_eq!(detect_file_type(Path::new("app.spec.ts")), FileType::Test);
assert_eq!(
detect_file_type(Path::new("benches/perf.rs")),
FileType::Benchmark
);
assert_eq!(
detect_file_type(Path::new("perf_bench.rs")),
FileType::Benchmark
);
assert_eq!(
detect_file_type(Path::new("examples/demo.rs")),
FileType::Example
);
assert_eq!(
detect_file_type(Path::new("demo_example.rs")),
FileType::Example
);
assert_eq!(
detect_file_type(Path::new("build.rs")),
FileType::BuildScript
);
assert_eq!(
detect_file_type(Path::new("README.md")),
FileType::Documentation
);
assert_eq!(
detect_file_type(Path::new("api.rst")),
FileType::Documentation
);
assert_eq!(
detect_file_type(Path::new("Cargo.toml")),
FileType::Configuration
);
assert_eq!(
detect_file_type(Path::new("config.yaml")),
FileType::Configuration
);
assert_eq!(
detect_file_type(Path::new("src/main.rs")),
FileType::Production
);
assert_eq!(detect_file_type(Path::new("lib.rs")), FileType::Production);
assert_eq!(detect_file_type(Path::new("app.py")), FileType::Production);
}
}