//! VBD (Volatility-Based Decomposition) and EBD (Experience-Based Decomposition) layer rules. use crate::error::ArchDiagnostic; use crate::rule::{ArchRule, FnContext}; // ── VBD-001: Accessor must not call Manager ─────────────────────────────────── /// VBD-001: @accessor functions must not call @manager functions. /// Accessors are read-only, stable-interface components; they must not depend /// on manager-layer orchestration logic. pub struct AccessorMustNotCallManager; impl ArchRule for AccessorMustNotCallManager { fn name(&self) -> &str { "VBD-001" } fn description(&self) -> &str { "@accessor must not call @manager functions (accessor must not depend on manager layer)" } fn check(&self, ctx: &FnContext<'_>) -> Vec { if !ctx.has_annotation("accessor") { return vec![]; } ctx.body_calls .iter() .filter(|call| ctx.callee_has_annotation(&call.callee, "manager")) .map(|call| ArchDiagnostic::error( self.name(), format!( "accessor '{}' calls manager '{}' — accessors must not depend on the manager layer", ctx.fn_name, call.callee ), Some(ctx.fn_name.to_string()), )) .collect() } } // ── VBD-002: Experience must not directly call Experience ───────────────────── /// VBD-002 / EBD-001: @experience functions must not call other @experience functions directly. /// Experiences should communicate via events, not direct calls, to preserve /// loose coupling between user-facing features. pub struct ExperienceMustNotCallExperience; impl ArchRule for ExperienceMustNotCallExperience { fn name(&self) -> &str { "VBD-002" } fn description(&self) -> &str { "@experience must not call another @experience directly (use events instead)" } fn check(&self, ctx: &FnContext<'_>) -> Vec { if !ctx.has_annotation("experience") { return vec![]; } ctx.body_calls .iter() .filter(|call| ctx.callee_has_annotation(&call.callee, "experience")) .map(|call| ArchDiagnostic::error( self.name(), format!( "experience '{}' directly calls experience '{}' — use an event instead", ctx.fn_name, call.callee ), Some(ctx.fn_name.to_string()), )) .collect() } } // ── VBD-003: Experience should return Result ──────────────────────────── /// VBD-003: @experience functions should return Result for proper error propagation. pub struct ExperienceShouldReturnResult; impl ArchRule for ExperienceShouldReturnResult { fn name(&self) -> &str { "VBD-003" } fn description(&self) -> &str { "@experience functions should return Result for proper error handling" } fn check(&self, ctx: &FnContext<'_>) -> Vec { if !ctx.has_annotation("experience") { return vec![]; } // Warn if return type doesn't include "Result" if !ctx.return_type_name.contains("Result") { return vec![ArchDiagnostic::warning( self.name(), format!( "experience '{}' returns '{}' instead of Result — experiences should propagate errors", ctx.fn_name, ctx.return_type_name ), Some(ctx.fn_name.to_string()), )]; } vec![] } }