raz_validation/
registry.rs1use crate::error::{ValidationError, ValidationResult};
4use crate::provider::{OptionDef, OptionProvider};
5use crate::suggestion::OptionSuggester;
6use crate::validation::ValidationConfig;
7use std::collections::HashMap;
8
9pub struct ValidationRegistry {
11 providers: Vec<Box<dyn OptionProvider>>,
12 suggester: OptionSuggester,
13}
14
15impl Default for ValidationRegistry {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl ValidationRegistry {
22 pub fn new() -> Self {
24 Self {
25 providers: Vec::new(),
26 suggester: OptionSuggester::new(),
27 }
28 }
29
30 pub fn register_provider(&mut self, provider: Box<dyn OptionProvider>) {
32 self.providers.push(provider);
33 }
34
35 pub fn get_providers(&self) -> &[Box<dyn OptionProvider>] {
37 &self.providers
38 }
39
40 pub fn get_options(&self, command: &str) -> Vec<OptionDef> {
42 let mut all_options = Vec::new();
43
44 for provider in &self.providers {
45 if provider.supports_command(command) {
46 all_options.extend(provider.get_options(command));
47 }
48 }
49
50 let mut seen = std::collections::HashSet::new();
52 all_options.retain(|opt| seen.insert(opt.name.clone()));
53
54 all_options
55 }
56
57 pub fn validate_option(
59 &self,
60 command: &str,
61 option: &str,
62 value: Option<&str>,
63 config: &ValidationConfig,
64 ) -> ValidationResult<()> {
65 for provider in &self.providers {
67 if provider.supports_command(command) {
68 let options = provider.get_options(command);
69 if options.iter().any(|opt| opt.name == option) {
70 return provider.validate(command, option, value);
71 }
72 }
73 }
74
75 if config.level.is_strict() {
77 let suggestions = self.suggest_option(command, option);
78 Err(ValidationError::unknown_option(
79 command,
80 option,
81 suggestions,
82 ))
83 } else {
84 Ok(())
86 }
87 }
88
89 pub fn suggest_option(&self, command: &str, option: &str) -> Vec<String> {
91 let all_options = self.get_options(command);
92 let option_names: Vec<&str> = all_options.iter().map(|opt| opt.name.as_str()).collect();
93
94 self.suggester.suggest_options(option, &option_names)
95 }
96
97 pub fn validate_conflicts(
99 &self,
100 command: &str,
101 options: &HashMap<String, Option<String>>,
102 ) -> ValidationResult<()> {
103 let option_defs = self.get_options(command);
104 let mut option_map: HashMap<String, &OptionDef> = HashMap::new();
105
106 for def in &option_defs {
107 option_map.insert(def.name.clone(), def);
108 }
109
110 for option_name in options.keys() {
112 if let Some(option_def) = option_map.get(option_name) {
113 for conflict in &option_def.conflicts_with {
114 if options.contains_key(conflict) {
115 return Err(ValidationError::conflict(option_name, conflict));
116 }
117 }
118 }
119 }
120
121 Ok(())
122 }
123
124 pub fn validate_requirements(
126 &self,
127 command: &str,
128 options: &HashMap<String, Option<String>>,
129 ) -> ValidationResult<()> {
130 let option_defs = self.get_options(command);
131 let mut option_map: HashMap<String, &OptionDef> = HashMap::new();
132
133 for def in &option_defs {
134 option_map.insert(def.name.clone(), def);
135 }
136
137 for option_name in options.keys() {
139 if let Some(option_def) = option_map.get(option_name) {
140 for required in &option_def.requires {
141 if !options.contains_key(required) {
142 return Err(ValidationError::missing_requirement(option_name, required));
143 }
144 }
145 }
146 }
147
148 Ok(())
149 }
150
151 pub fn get_provider(&self, name: &str) -> Option<&dyn OptionProvider> {
153 self.providers
154 .iter()
155 .find(|p| p.name() == name)
156 .map(|p| p.as_ref())
157 }
158
159 pub fn supports_command(&self, command: &str) -> bool {
161 self.providers.iter().any(|p| p.supports_command(command))
162 }
163}