Skip to main content

fraiseql_core/validation/
inheritance.rs

1//! Validation inheritance for input types.
2//!
3//! This module provides support for validation rule inheritance, allowing child
4//! input types to inherit and override validation rules from parent types.
5//!
6//! # Examples
7//!
8//! ```
9//! use fraiseql_core::validation::{CompiledPattern, ValidationRule, InheritanceMode, inherit_validation_rules};
10//!
11//! // Parent: UserInput with required email and minLength 5
12//! // Child: AdminUserInput extends UserInput with additional admin-only rules
13//!
14//! let parent_rules = vec![
15//!     ValidationRule::Pattern { pattern: CompiledPattern::new("^.+@.+$").expect("valid regex"), message: None },
16//!     ValidationRule::Length { min: Some(5), max: None },
17//! ];
18//!
19//! let child_rules = vec![
20//!     ValidationRule::Required,
21//!     ValidationRule::Pattern { pattern: CompiledPattern::new("^admin_.+$").expect("valid regex"), message: None },
22//! ];
23//!
24//! let inherited = inherit_validation_rules(&parent_rules, &child_rules, InheritanceMode::Merge);
25//! assert_eq!(inherited.len(), 4);
26//! ```
27
28use std::collections::HashMap;
29
30use crate::validation::rules::ValidationRule;
31
32/// Determines how child validation rules interact with parent rules.
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[non_exhaustive]
35pub enum InheritanceMode {
36    /// Child rules completely override parent rules (no inheritance)
37    Override,
38    /// Parent and child rules are merged (all apply)
39    Merge,
40    /// Child rules are applied first, then parent rules
41    ChildFirst,
42    /// Parent rules are applied first, then child rules
43    ParentFirst,
44}
45
46impl InheritanceMode {
47    /// Get a human-readable description.
48    #[must_use]
49    pub const fn description(&self) -> &'static str {
50        match self {
51            Self::Override => "Child rules override parent rules completely",
52            Self::Merge => "All parent and child rules apply (union)",
53            Self::ChildFirst => "Child rules applied first, then parent rules",
54            Self::ParentFirst => "Parent rules applied first, then child rules",
55        }
56    }
57}
58
59/// Metadata about a validation rule for inheritance tracking.
60#[derive(Debug, Clone)]
61pub struct RuleMetadata {
62    /// The validation rule
63    pub rule:         ValidationRule,
64    /// Whether this rule can be overridden by child types
65    pub overrideable: bool,
66    /// Whether this rule is inherited from a parent type
67    pub inherited:    bool,
68    /// The source type name for tracking
69    pub source:       String,
70}
71
72impl RuleMetadata {
73    /// Create a new rule metadata from a validation rule.
74    pub fn new(rule: ValidationRule, source: impl Into<String>) -> Self {
75        Self {
76            rule,
77            overrideable: true,
78            inherited: false,
79            source: source.into(),
80        }
81    }
82
83    /// Mark this rule as non-overrideable.
84    #[must_use]
85    pub const fn non_overrideable(mut self) -> Self {
86        self.overrideable = false;
87        self
88    }
89
90    /// Mark this rule as inherited.
91    #[must_use]
92    pub const fn as_inherited(mut self) -> Self {
93        self.inherited = true;
94        self
95    }
96}
97
98/// Validation rule registry tracking inheritance relationships.
99#[derive(Debug, Clone, Default)]
100pub struct ValidationRuleRegistry {
101    /// Rules by type name
102    pub(crate) rules_by_type: HashMap<String, Vec<RuleMetadata>>,
103    /// Parent type references
104    parent_types:             HashMap<String, String>,
105}
106
107impl ValidationRuleRegistry {
108    /// Create a new validation rule registry.
109    #[must_use]
110    pub fn new() -> Self {
111        Self {
112            rules_by_type: HashMap::new(),
113            parent_types:  HashMap::new(),
114        }
115    }
116
117    /// Register rules for a type.
118    pub fn register_type(&mut self, type_name: impl Into<String>, rules: Vec<RuleMetadata>) {
119        self.rules_by_type.insert(type_name.into(), rules);
120    }
121
122    /// Set the parent type for inheritance.
123    pub fn set_parent(&mut self, child_type: impl Into<String>, parent_type: impl Into<String>) {
124        self.parent_types.insert(child_type.into(), parent_type.into());
125    }
126
127    /// Get rules for a type, including inherited rules.
128    #[must_use]
129    pub fn get_rules(&self, type_name: &str, mode: InheritanceMode) -> Vec<RuleMetadata> {
130        let mut rules = Vec::new();
131
132        // Get parent rules if applicable
133        if let Some(parent_name) = self.parent_types.get(type_name) {
134            let parent_rules = self.get_rules(parent_name, mode);
135            rules.extend(parent_rules.iter().map(|r| r.clone().as_inherited()));
136        }
137
138        // Get own rules
139        if let Some(own_rules) = self.rules_by_type.get(type_name) {
140            match mode {
141                InheritanceMode::Override => {
142                    // Child rules completely override parent rules
143                    return own_rules.clone();
144                },
145                InheritanceMode::Merge => {
146                    // Add all own rules that don't override parent rules
147                    for own_rule in own_rules {
148                        rules.push(own_rule.clone());
149                    }
150                },
151                InheritanceMode::ChildFirst => {
152                    // Keep order: own rules first (reverse order since we prepend)
153                    let mut result = own_rules.clone();
154                    result.extend(rules);
155                    return result;
156                },
157                InheritanceMode::ParentFirst => {
158                    // Keep order: parent rules first
159                    rules.extend(own_rules.clone());
160                },
161            }
162        }
163
164        rules
165    }
166
167    /// Get the parent type name if one exists.
168    #[must_use]
169    pub fn get_parent(&self, type_name: &str) -> Option<&str> {
170        self.parent_types.get(type_name).map(|s| s.as_str())
171    }
172
173    /// Check if a type has a parent.
174    #[must_use]
175    pub fn has_parent(&self, type_name: &str) -> bool {
176        self.parent_types.contains_key(type_name)
177    }
178}
179
180/// Inherit validation rules from parent to child.
181///
182/// # Arguments
183/// * `parent_rules` - Rules from the parent type
184/// * `child_rules` - Rules defined on the child type
185/// * `mode` - How to combine parent and child rules
186///
187/// # Returns
188/// Combined rules based on the inheritance mode
189#[must_use]
190pub fn inherit_validation_rules(
191    parent_rules: &[ValidationRule],
192    child_rules: &[ValidationRule],
193    mode: InheritanceMode,
194) -> Vec<ValidationRule> {
195    match mode {
196        InheritanceMode::Override => {
197            // Child rules completely replace parent rules
198            child_rules.to_vec()
199        },
200        InheritanceMode::Merge => {
201            // Combine all rules from both parent and child
202            let mut combined = parent_rules.to_vec();
203            combined.extend_from_slice(child_rules);
204            combined
205        },
206        InheritanceMode::ChildFirst => {
207            // Child rules first, then parent rules
208            let mut combined = child_rules.to_vec();
209            combined.extend_from_slice(parent_rules);
210            combined
211        },
212        InheritanceMode::ParentFirst => {
213            // Parent rules first, then child rules
214            let mut combined = parent_rules.to_vec();
215            combined.extend_from_slice(child_rules);
216            combined
217        },
218    }
219}
220
221/// Check if child type has valid inheritance from parent type.
222///
223/// # Arguments
224/// * `_child_name` - Name of the child type
225/// * `parent_name` - Name of the parent type
226/// * `registry` - The validation rule registry
227///
228/// # Errors
229///
230/// Returns an error string if the parent type is not found or contains circular inheritance.
231pub fn validate_inheritance(
232    _child_name: &str,
233    parent_name: &str,
234    registry: &ValidationRuleRegistry,
235) -> Result<(), String> {
236    // Check that parent type exists in registry
237    if !registry.rules_by_type.contains_key(parent_name) {
238        return Err(format!("Parent type '{}' not found in validation registry", parent_name));
239    }
240
241    // Check for circular inheritance
242    let mut visited = std::collections::HashSet::new();
243    let mut current = Some(parent_name.to_string());
244
245    while let Some(type_name) = current {
246        if visited.contains(&type_name) {
247            return Err(format!(
248                "Circular inheritance detected: '{}' inherits from itself",
249                type_name
250            ));
251        }
252        visited.insert(type_name.clone());
253
254        current = registry.get_parent(&type_name).map(|s| s.to_string());
255    }
256
257    Ok(())
258}