raz_validation/
provider.rs1use crate::error::{ValidationError, ValidationResult};
4use serde::{Deserialize, Serialize};
5
6pub trait OptionProvider: Send + Sync {
8 fn name(&self) -> &str;
10
11 fn get_options(&self, command: &str) -> Vec<OptionDef>;
13
14 fn validate(&self, command: &str, option: &str, value: Option<&str>) -> ValidationResult<()>;
16
17 fn get_commands(&self) -> Vec<String>;
19
20 fn supports_command(&self, command: &str) -> bool {
22 self.get_commands().contains(&command.to_string())
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct OptionDef {
29 pub name: String,
31
32 pub value_type: OptionValueType,
34
35 pub description: String,
37
38 pub conflicts_with: Vec<String>,
40
41 pub requires: Vec<String>,
43
44 pub deprecated: Option<String>,
46
47 pub repeatable: bool,
49}
50
51impl OptionDef {
52 pub fn flag(name: impl Into<String>, description: impl Into<String>) -> Self {
54 Self {
55 name: name.into(),
56 value_type: OptionValueType::Flag,
57 description: description.into(),
58 conflicts_with: Vec::new(),
59 requires: Vec::new(),
60 deprecated: None,
61 repeatable: false,
62 }
63 }
64
65 pub fn single(
67 name: impl Into<String>,
68 description: impl Into<String>,
69 validator: ValueValidator,
70 ) -> Self {
71 Self {
72 name: name.into(),
73 value_type: OptionValueType::Single(validator),
74 description: description.into(),
75 conflicts_with: Vec::new(),
76 requires: Vec::new(),
77 deprecated: None,
78 repeatable: false,
79 }
80 }
81
82 pub fn multiple(
84 name: impl Into<String>,
85 description: impl Into<String>,
86 validator: ValueValidator,
87 ) -> Self {
88 Self {
89 name: name.into(),
90 value_type: OptionValueType::Multiple(validator),
91 description: description.into(),
92 conflicts_with: Vec::new(),
93 requires: Vec::new(),
94 deprecated: None,
95 repeatable: false,
96 }
97 }
98
99 pub fn conflicts_with(mut self, options: Vec<String>) -> Self {
101 self.conflicts_with = options;
102 self
103 }
104
105 pub fn requires(mut self, options: Vec<String>) -> Self {
107 self.requires = options;
108 self
109 }
110
111 pub fn deprecated(mut self, message: impl Into<String>) -> Self {
113 self.deprecated = Some(message.into());
114 self
115 }
116
117 pub fn repeatable(mut self) -> Self {
119 self.repeatable = true;
120 self
121 }
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub enum OptionValueType {
127 Flag,
129
130 Single(ValueValidator),
132
133 Multiple(ValueValidator),
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
139pub enum ValueValidator {
140 Any,
142
143 Number,
145
146 Enum(Vec<String>),
148
149 Regex(String),
151
152 FilePath,
154
155 DirectoryPath,
157
158 Custom(String),
160}
161
162impl ValueValidator {
163 pub fn validate(&self, value: &str) -> ValidationResult<()> {
165 match self {
166 Self::Any => Ok(()),
167
168 Self::Number => value
169 .parse::<f64>()
170 .map(|_| ())
171 .map_err(|_| ValidationError::invalid_value("", value, "must be a number")),
172
173 Self::Enum(valid_values) => {
174 if valid_values.contains(&value.to_string()) {
175 Ok(())
176 } else {
177 Err(ValidationError::invalid_value(
178 "",
179 value,
180 format!("must be one of: {}", valid_values.join(", ")),
181 ))
182 }
183 }
184
185 Self::Regex(pattern) => {
186 let regex = regex::Regex::new(pattern).map_err(|_| {
187 ValidationError::invalid_value("", value, "invalid regex pattern")
188 })?;
189
190 if regex.is_match(value) {
191 Ok(())
192 } else {
193 Err(ValidationError::invalid_value(
194 "",
195 value,
196 format!("must match pattern: {pattern}"),
197 ))
198 }
199 }
200
201 Self::FilePath => {
202 if value.is_empty() {
204 Err(ValidationError::invalid_value(
205 "",
206 value,
207 "file path cannot be empty",
208 ))
209 } else {
210 Ok(())
211 }
212 }
213
214 Self::DirectoryPath => {
215 if value.is_empty() {
217 Err(ValidationError::invalid_value(
218 "",
219 value,
220 "directory path cannot be empty",
221 ))
222 } else {
223 Ok(())
224 }
225 }
226
227 Self::Custom(_name) => {
228 Ok(())
230 }
231 }
232 }
233}
234
235pub mod cargo;
237pub mod leptos;