ignore_functions = [
"main",
"run",
"visit_*",
]
exclude_files = ["examples/**"]
strict_closures = false
strict_iterator_chains = false
allow_recursion = false
strict_error_propagation = false
max_suppression_ratio = 0.10
[complexity]
enabled = true
allow_expect = false
[duplicates]
enabled = true
[boilerplate]
enabled = true
[srp]
enabled = true
[coupling]
enabled = true
[structural]
enabled = true
[test_quality]
enabled = true
[weights]
iosp = 0.22
complexity = 0.18
dry = 0.13
srp = 0.18
coupling = 0.09
test_quality = 0.10
architecture = 0.10
[architecture]
enabled = true
[architecture.layers]
order = ["domain", "port", "infrastructure", "analysis", "application"]
unmatched_behavior = "strict_error"
[architecture.layers.domain]
paths = ["src/domain/**"]
[architecture.layers.port]
paths = ["src/ports/**"]
[architecture.layers.infrastructure]
paths = [
"src/adapters/config/**",
"src/adapters/source/**",
"src/adapters/suppression/**",
]
[architecture.layers.analysis]
paths = [
"src/adapters/analyzers/**",
"src/adapters/shared/**",
"src/adapters/report/**",
]
[architecture.layers.application]
paths = ["src/app/**"]
[architecture.reexport_points]
paths = [
"src/lib.rs",
"src/main.rs",
"src/adapters/mod.rs",
"src/bin/**",
"src/cli/**",
"tests/**",
]
[architecture.external_crates]
[[architecture.forbidden]]
from = "src/adapters/analyzers/iosp/**"
to = "src/adapters/analyzers/**"
except = ["src/adapters/analyzers/iosp/**"]
reason = "Dimension-Analyzer kennen sich nicht gegenseitig"
[[architecture.forbidden]]
from = "src/adapters/analyzers/complexity/**"
to = "src/adapters/analyzers/**"
except = [
"src/adapters/analyzers/complexity/**",
"src/adapters/analyzers/iosp/**",
]
reason = "Dimension-Analyzer kennen sich nicht gegenseitig"
[[architecture.forbidden]]
from = "src/adapters/analyzers/dry/**"
to = "src/adapters/analyzers/**"
except = [
"src/adapters/analyzers/dry/**",
"src/adapters/analyzers/iosp/**",
]
reason = "Dimension-Analyzer kennen sich nicht gegenseitig"
[[architecture.forbidden]]
from = "src/adapters/analyzers/srp/**"
to = "src/adapters/analyzers/**"
except = [
"src/adapters/analyzers/srp/**",
"src/adapters/analyzers/iosp/**",
]
reason = "Dimension-Analyzer kennen sich nicht gegenseitig"
[[architecture.forbidden]]
from = "src/adapters/analyzers/coupling/**"
to = "src/adapters/analyzers/**"
except = [
"src/adapters/analyzers/coupling/**",
"src/adapters/analyzers/iosp/**",
]
reason = "Dimension-Analyzer kennen sich nicht gegenseitig"
[[architecture.forbidden]]
from = "src/adapters/analyzers/tq/**"
to = "src/adapters/analyzers/**"
except = [
"src/adapters/analyzers/tq/**",
"src/adapters/analyzers/dry/**",
"src/adapters/analyzers/iosp/**",
]
reason = "Dimension-Analyzer kennen sich nicht gegenseitig (TQ ist derivativ)"
[[architecture.forbidden]]
from = "src/adapters/analyzers/structural/**"
to = "src/adapters/analyzers/**"
except = [
"src/adapters/analyzers/structural/**",
"src/adapters/analyzers/iosp/**",
]
reason = "Dimension-Analyzer kennen sich nicht gegenseitig"
[[architecture.forbidden]]
from = "src/adapters/analyzers/architecture/**"
to = "src/adapters/analyzers/**"
except = ["src/adapters/analyzers/architecture/**"]
reason = "Dimension-Analyzer kennen sich nicht gegenseitig"
[[architecture.forbidden]]
from = "src/adapters/analyzers/**"
to = "src/adapters/report/**"
reason = "Analyzer erzeugen Findings; Reporter konsumieren sie separat"
[[architecture.forbidden]]
from = "src/adapters/**"
to = "src/app/**"
reason = "Adapter kennt Ports + Domain, aber keine Application-Use-Cases"
[[architecture.pattern]]
name = "no_syn_in_domain"
forbid_path_prefix = ["syn::", "proc_macro2::", "quote::"]
forbidden_in = ["src/domain/**"]
reason = "Domain-Typen kennen keine AST-Darstellung"
[[architecture.pattern]]
name = "no_glob_imports_in_domain"
forbid_glob_import = true
forbidden_in = ["src/domain/**"]
reason = "Glob-Imports (use foo::*) verstecken Layer-Tunneling durch Re-Exports"
[[architecture.pattern]]
name = "filesystem_only_in_adapters"
forbid_path_prefix = ["walkdir::", "notify::"]
allowed_in = ["src/adapters/**"]
reason = "Filesystem-Zugriff läuft über Adapter-Ports"
[[architecture.pattern]]
name = "serialization_only_in_adapters"
forbid_path_prefix = ["toml::", "serde_json::", "toon_encode::"]
allowed_in = ["src/adapters/**", "**/tests/**"]
reason = "Serde-derives überall erlaubt, aber Serialisierungs-I/O nur in Adaptern"
[[architecture.pattern]]
name = "rayon_only_in_adapters"
forbid_path_prefix = ["rayon::"]
allowed_in = ["src/adapters/**"]
reason = "Parallelisierung ist Adapter-Implementierung, nicht Domain-/App-Concern"
[[architecture.pattern]]
name = "globset_only_in_adapters"
forbid_path_prefix = ["globset::", "regex::"]
allowed_in = ["src/adapters/**", "**/tests/**"]
reason = "Glob- und Regex-Matching leben in Adaptern"
[[architecture.pattern]]
name = "no_panic_helpers_in_production"
forbid_method_call = ["unwrap", "expect"]
forbidden_in = ["src/**"]
except = ["**/tests/**"]
reason = "Production propagiert Fehler typisiert statt zu panicken"
[[architecture.pattern]]
name = "no_stdout_in_library_code"
forbid_macro_call = ["println", "print", "dbg"]
forbidden_in = ["src/**"]
except = [
"src/main.rs",
"src/lib.rs",
"src/bin/**",
"src/cli/**",
"src/adapters/report/**",
"**/tests/**",
]
reason = "Stdout-Dispatch nur in Report-Adaptern und Composition Root"
[[architecture.pattern]]
name = "no_stderr_in_pure_layers"
forbid_macro_call = ["eprintln", "eprint"]
forbidden_in = ["src/domain/**", "src/ports/**"]
reason = "Domain und Ports propagieren Fehler über Result, nicht über stderr"
[[architecture.pattern]]
name = "no_panic_macros_in_production"
forbid_macro_call = ["panic", "todo", "unimplemented", "unreachable"]
forbidden_in = ["src/**"]
except = ["**/tests/**"]
reason = "Production-Code hat typisierte Errors, keine Panics"
[[architecture.pattern]]
name = "clap_only_in_composition_root"
forbid_path_prefix = ["clap::", "clap_complete::"]
allowed_in = ["src/main.rs", "src/lib.rs", "src/bin/**", "src/cli/**", "**/tests/**"]
reason = "CLI-Parsing ist Verdrahtungs-Concern im Composition Root"
[[architecture.pattern]]
name = "anyhow_only_at_boundary"
forbid_path_prefix = ["anyhow::"]
allowed_in = ["src/main.rs", "src/lib.rs", "src/bin/**", "**/tests/**"]
reason = "Typisierte Errors in Domain/App/Ports/Adapter; anyhow nur am Rand"
[[architecture.pattern]]
name = "no_boxed_error"
forbid_path_prefix = ["Box<dyn std::error::Error", "Box<dyn Error"]
forbidden_in = ["src/**"]
except = ["**/tests/**"]
reason = "Typisierte Errors; kein Zerfall zu dyn Error"
[[architecture.pattern]]
name = "no_unsafe"
forbid_item_kind = ["unsafe_fn", "unsafe_impl", "extern_c_block"]
forbidden_in = ["src/**"]
reason = "Rustqual ist reiner Analyser, kein FFI, kein unsafe"
[[architecture.pattern]]
name = "no_inline_cfg_test_module"
forbid_item_kind = ["inline_cfg_test_module"]
forbidden_in = ["src/**"]
reason = "Tests in tests/-Schwester-Bäumen, nicht inline — ehrliche Modul-Längen-Metrik"
[[architecture.pattern]]
name = "no_top_level_cfg_test_item"
forbid_item_kind = ["top_level_cfg_test_item"]
forbidden_in = ["src/**"]
reason = "Test-Helpers in Companion-Files oder mod tests, nicht lose zwischen Production-Code"
[[architecture.trait_contract]]
name = "port_traits"
scope = "src/ports/**"
receiver_may_be = ["shared_ref"]
forbidden_return_type_contains = [
"anyhow::",
"Box<dyn",
]
forbidden_error_variant_contains = [
"syn::",
"toml::",
"serde_json::",
"walkdir::",
"notify::",
"rayon::",
"globset::",
"regex::",
]
must_be_object_safe = true
required_supertraits_contain = ["Send", "Sync"]
[report]
aggregation = "loc_weighted"