feat: implement all P1/P2/P3/P4 improvements from issue backlog
Some checks failed
CI / Check / Test (push) Failing after 1m33s
Architecture Docs / Generate diagrams (push) Successful in 3m21s

P1 correctness:
- filter test files by default (--include-tests to opt in)
- per-module diagrams show cross-module dependency arrows
- qualified type names (Module::TypeName) fix false edges from duplicate names

P2 output richness:
- method parameter types and return types in class diagrams (Rust + Python)
- Python pyproject.toml project analyzer (--level project for monorepos)

P3 unique value:
- boundary rules in archlens.toml ([rules] allow/deny, --strict enforcement)

P4 nice to have:
- dependency weight labels on module arrows (--no-weights to disable)
- --watch mode with 500ms debounce
- D2 renderer adapter (--format d2)
- interactive self-contained HTML viewer (--format html)
- git-aware incremental analysis (--since <ref>)
This commit is contained in:
2026-06-17 09:50:50 +02:00
parent 27197062eb
commit fdd85011a4
42 changed files with 2767 additions and 92 deletions

View File

@@ -53,6 +53,10 @@ fn analyzes_rust_project_and_writes_mermaid_to_file() {
config: None,
scope: None,
exclude: vec![],
include_tests: false,
no_weights: false,
watch: false,
since: None,
split_by_module: false,
strict: false,
check: false,
@@ -83,6 +87,10 @@ fn works_without_config_file() {
config: None,
scope: None,
exclude: vec![],
include_tests: false,
no_weights: false,
watch: false,
since: None,
split_by_module: false,
strict: false,
check: false,
@@ -108,6 +116,10 @@ fn split_by_module_writes_overview_and_per_module_files() {
config: None,
scope: None,
exclude: vec![],
include_tests: false,
no_weights: false,
watch: false,
since: None,
split_by_module: true,
strict: false,
check: false,
@@ -131,3 +143,59 @@ fn split_by_module_writes_overview_and_per_module_files() {
"should have overview + at least one module file"
);
}
fn create_cross_module_project(dir: &std::path::Path) {
fs::create_dir_all(dir.join("src/app")).unwrap();
fs::create_dir_all(dir.join("src/domain")).unwrap();
fs::write(
dir.join("src/domain/order.rs"),
"pub struct Order { pub id: u64 }\n",
)
.unwrap();
fs::write(
dir.join("src/app/service.rs"),
"use crate::domain::Order;\npub struct OrderService { order: Order }\n",
)
.unwrap();
}
#[test]
fn per_module_file_shows_cross_module_dependency_arrows() {
let project = tempfile::tempdir().unwrap();
create_cross_module_project(project.path());
let output_dir = tempfile::tempdir().unwrap();
run(archlens::CliArgs {
command: None,
path: project.path().to_path_buf(),
level: "type".to_string(),
format: "mermaid".to_string(),
output: Some(output_dir.path().to_str().unwrap().to_string()),
config: None,
scope: None,
exclude: vec![],
include_tests: false,
no_weights: false,
watch: false,
since: None,
split_by_module: true,
strict: false,
check: false,
verbose: 0,
})
.unwrap();
let app_file = output_dir.path().join("app.mmd");
assert!(app_file.exists(), "app.mmd should exist");
let content = fs::read_to_string(&app_file).unwrap();
assert!(
content.contains("<<module>>"),
"per-module file should contain external module placeholder: {content}"
);
assert!(
content.contains("domain"),
"per-module file should reference the domain module: {content}"
);
}