use crate::error::ValidationResult;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ValidationConfig {
#[serde(default)]
pub architecture: ArchitectureConfig,
#[serde(default)]
pub semantic: SemanticConfig,
#[serde(default)]
pub intent: IntentConfig,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ArchitectureConfig {
#[serde(default)]
pub layers: Vec<Layer>,
#[serde(default = "default_true")]
pub check_circular_deps: bool,
#[serde(default = "default_true")]
pub enforce_layers: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Layer {
pub name: String,
pub paths: Vec<String>,
#[serde(default)]
pub can_depend_on: Vec<String>,
#[serde(default)]
pub prohibited_dependencies: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticConfig {
#[serde(default = "default_true")]
pub check_breaking_changes: bool,
#[serde(default = "default_true")]
pub breaking_changes_error: bool,
#[serde(default = "default_true")]
pub check_visibility: bool,
#[serde(default)]
pub review_new_public_api: bool,
#[serde(default)]
pub complexity: ComplexityConfig,
}
impl Default for SemanticConfig {
fn default() -> Self {
Self {
check_breaking_changes: true,
breaking_changes_error: true,
check_visibility: true,
review_new_public_api: false,
complexity: ComplexityConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplexityConfig {
#[serde(default = "default_complexity")]
pub max_cyclomatic: u32,
#[serde(default = "default_cognitive")]
pub max_cognitive: u32,
#[serde(default = "default_delta")]
pub max_complexity_increase: i32,
#[serde(default)]
pub complexity_error: bool,
}
impl Default for ComplexityConfig {
fn default() -> Self {
Self {
max_cyclomatic: 15,
max_cognitive: 25,
max_complexity_increase: 10,
complexity_error: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IntentConfig {
#[serde(default = "default_true")]
pub validate_scope: bool,
#[serde(default)]
pub require_rationale_for_large_changes: bool,
#[serde(default = "default_large_threshold")]
pub large_change_threshold: usize,
}
impl Default for IntentConfig {
fn default() -> Self {
Self {
validate_scope: true,
require_rationale_for_large_changes: false,
large_change_threshold: 10,
}
}
}
fn default_true() -> bool {
true
}
fn default_complexity() -> u32 {
15
}
fn default_cognitive() -> u32 {
25
}
fn default_delta() -> i32 {
10
}
fn default_large_threshold() -> usize {
10
}
impl ValidationConfig {
pub fn load(path: &Path) -> ValidationResult<Self> {
let content = std::fs::read_to_string(path)?;
let config: Self = serde_yaml::from_str(&content)?;
Ok(config)
}
pub fn load_or_default(smelt_dir: &Path) -> Self {
let crucible_path = smelt_dir.join("crucible.yaml");
if crucible_path.exists() {
if let Ok(config) = Self::load(&crucible_path) {
return config;
}
}
let validation_path = smelt_dir.join("validation.yaml");
if validation_path.exists() {
if let Ok(config) = Self::load(&validation_path) {
return config;
}
}
Self::default()
}
pub fn strict() -> Self {
Self {
architecture: ArchitectureConfig {
check_circular_deps: true,
enforce_layers: true,
layers: Vec::new(),
},
semantic: SemanticConfig {
check_breaking_changes: true,
breaking_changes_error: true,
check_visibility: true,
review_new_public_api: true,
complexity: ComplexityConfig {
max_cyclomatic: 10,
max_cognitive: 15,
max_complexity_increase: 5,
complexity_error: true,
},
},
intent: IntentConfig {
validate_scope: true,
require_rationale_for_large_changes: true,
large_change_threshold: 5,
},
}
}
}