use serde::Deserialize;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ArchitectureConfig {
pub layers: Vec<Layer>,
pub dependency_rules: Vec<DependencyRule>,
#[serde(default)]
pub package_policies: Vec<PackagePolicy>,
#[serde(default)]
pub ignore_patterns: Vec<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Layer {
pub name: String,
pub patterns: Vec<String>,
#[serde(default)]
pub namespace_patterns: Vec<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct DependencyRule {
pub from: String,
pub to: String,
pub allowed: bool,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct PackagePolicy {
pub layer: String,
pub forbidden: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize_full_config() {
let toml = r#"
[[layers]]
name = "Presentation"
patterns = ["*.Api"]
[[layers]]
name = "Domain"
patterns = ["*.Domain", "*.Core"]
[[dependency_rules]]
from = "Presentation"
to = "Domain"
allowed = true
[[package_policies]]
layer = "Domain"
forbidden = ["EntityFramework"]
"#;
let cfg: ArchitectureConfig = toml::from_str(toml).unwrap();
assert_eq!(cfg.layers.len(), 2);
assert_eq!(cfg.layers[0].name, "Presentation");
assert_eq!(cfg.dependency_rules.len(), 1);
assert!(cfg.dependency_rules[0].allowed);
assert_eq!(cfg.package_policies[0].forbidden, vec!["EntityFramework"]);
}
#[test]
fn package_policies_defaults_to_empty_when_absent() {
let toml = r#"
dependency_rules = []
[[layers]]
name = "A"
patterns = ["*"]
"#;
let cfg: ArchitectureConfig = toml::from_str(toml).unwrap();
assert!(cfg.package_policies.is_empty());
}
#[test]
fn dependency_rule_allowed_field_deserialized_correctly() {
let toml = r#"
layers = []
[[dependency_rules]]
from = "A"
to = "B"
allowed = false
[[dependency_rules]]
from = "C"
to = "D"
allowed = true
"#;
let cfg: ArchitectureConfig = toml::from_str(toml).unwrap();
assert!(!cfg.dependency_rules[0].allowed);
assert!(cfg.dependency_rules[1].allowed);
}
}