protify/validators/
message.rs1mod builder;
2pub use builder::MessageValidatorBuilder;
3
4use super::*;
5
6pub trait ValidatedMessage: ProtoValidation + Default + Clone {
8 #[inline]
14 fn validate_all(&self) -> Result<(), ValidationErrors> {
15 if !Self::HAS_DEFAULT_VALIDATOR {
16 return Ok(());
17 }
18
19 let mut ctx = ValidationCtx {
20 field_context: None,
21 parent_elements: vec![],
22 violations: ValidationErrors::new(),
23 fail_fast: false,
24 };
25
26 let _ = self.validate_with_ctx(&mut ctx);
27
28 if ctx.violations.is_empty() {
29 Ok(())
30 } else {
31 Err(ctx.violations)
32 }
33 }
34
35 #[inline]
40 fn validate(&self) -> Result<(), ValidationErrors> {
41 if !Self::HAS_DEFAULT_VALIDATOR {
42 return Ok(());
43 }
44
45 let mut ctx = ValidationCtx::default();
46
47 let _ = self.validate_with_ctx(&mut ctx);
48
49 if ctx.violations.is_empty() {
50 Ok(())
51 } else {
52 Err(ctx.violations)
53 }
54 }
55
56 #[inline]
61 fn is_valid(&self) -> bool {
62 if Self::HAS_DEFAULT_VALIDATOR {
63 self.validate().is_ok()
64 } else {
65 true
66 }
67 }
68
69 #[inline]
74 fn validated(self) -> Result<Self, ValidationErrors> {
75 if !Self::HAS_DEFAULT_VALIDATOR {
76 return Ok(self);
77 }
78
79 match self.validate() {
80 Ok(()) => Ok(self),
81 Err(e) => Err(e),
82 }
83 }
84
85 fn validate_with_ctx(&self, ctx: &mut ValidationCtx) -> ValidationResult;
88}
89
90#[non_exhaustive]
92#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94pub struct MessageValidator {
95 pub cel: Vec<CelProgram>,
97
98 pub ignore: Ignore,
100
101 pub required: bool,
103
104 pub required_error_message: Option<FixedStr>,
106}
107
108impl<T, S: builder::State> ValidatorBuilderFor<T> for MessageValidatorBuilder<S>
109where
110 T: ValidatedMessage + PartialEq + TryIntoCel,
111{
112 type Validator = MessageValidator;
113
114 #[inline]
115 fn build_validator(self) -> Self::Validator {
116 self.build()
117 }
118}
119
120impl<T> Validator<T> for MessageValidator
121where
122 T: ValidatedMessage + PartialEq + TryIntoCel,
123{
124 type Target = T;
125
126 #[cfg(feature = "cel")]
127 #[inline(never)]
128 #[cold]
129 fn check_cel_programs_with(&self, val: Self::Target) -> Result<(), Vec<CelError>> {
130 if self.cel.is_empty() {
131 Ok(())
132 } else {
133 test_programs(&self.cel, val)
134 }
135 }
136
137 #[cfg(feature = "cel")]
138 #[inline(never)]
139 #[cold]
140 #[doc(hidden)]
141 fn __check_cel_programs(&self) -> Result<(), Vec<CelError>> {
142 <Self as Validator<T>>::check_cel_programs_with(self, Self::Target::default())
143 }
144
145 #[doc(hidden)]
146 #[inline(never)]
147 #[cold]
148 fn __cel_rules(&self) -> Vec<CelRule> {
149 self.cel
150 .iter()
151 .map(|p| p.rule().clone())
152 .collect()
153 }
154
155 #[inline(never)]
156 #[cold]
157 fn check_consistency(&self) -> Result<(), Vec<ConsistencyError>> {
158 let mut errors = Vec::new();
159
160 #[cfg(feature = "cel")]
161 if let Err(e) = <Self as Validator<T>>::__check_cel_programs(self) {
162 errors.extend(e.into_iter().map(ConsistencyError::from));
163 }
164
165 if errors.is_empty() {
166 Ok(())
167 } else {
168 Err(errors)
169 }
170 }
171
172 fn execute_validation(
173 &self,
174 ctx: &mut ValidationCtx,
175 val: Option<&Self::Target>,
176 ) -> ValidationResult {
177 handle_ignore_always!(&self.ignore);
178 handle_ignore_if_zero_value!(&self.ignore, val.is_none());
179
180 let mut is_valid = IsValid::Yes;
181
182 if let Some(val) = val {
183 if T::HAS_DEFAULT_VALIDATOR {
184 if let Some(field_context) = &mut ctx.field_context {
185 ctx.parent_elements
186 .push(field_context.as_path_element());
187 }
188
189 is_valid &= val.validate_with_ctx(ctx)?;
190
191 if ctx.field_context.is_some() {
192 ctx.parent_elements.pop();
193 }
194 }
195
196 #[cfg(feature = "cel")]
197 if !self.cel.is_empty() {
198 let cel_ctx = ProgramsExecutionCtx {
199 programs: &self.cel,
200 value: val.clone(),
201 ctx,
202 };
203
204 is_valid &= cel_ctx.execute_programs()?;
205 }
206 } else if self.required {
207 is_valid &= ctx.add_required_violation(
208 self.required_error_message
209 .as_ref()
210 .map(|e| e.to_string()),
211 )?;
212 }
213
214 Ok(is_valid)
215 }
216
217 #[inline(never)]
218 #[cold]
219 fn schema(&self) -> Option<ValidatorSchema> {
220 Some(ValidatorSchema {
221 schema: self.clone().into(),
222 cel_rules: <Self as Validator<T>>::__cel_rules(self),
223 imports: vec!["buf/validate/validate.proto".into()],
224 })
225 }
226}
227
228impl From<MessageValidator> for ProtoOption {
229 #[inline(never)]
230 #[cold]
231 fn from(validator: MessageValidator) -> Self {
232 let mut rules = OptionMessageBuilder::new();
233
234 rules
235 .add_cel_options(validator.cel)
236 .set_required(validator.required)
237 .set_ignore(validator.ignore);
238
239 Self {
240 name: "(buf.validate.field)".into(),
241 value: OptionValue::Message(rules.into()),
242 }
243 }
244}
245
246#[non_exhaustive]
250#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
251#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
252pub struct CelValidator {
253 pub programs: Vec<CelProgram>,
254}
255
256impl CelValidator {
257 #[must_use]
259 #[inline]
260 pub fn cel(mut self, program: CelProgram) -> Self {
261 self.programs.push(program);
262 self
263 }
264}
265
266impl<T> Validator<T> for CelValidator
267where
268 T: ValidatedMessage + PartialEq + TryIntoCel + Default + Clone,
269{
270 type Target = T;
271
272 #[cfg(feature = "cel")]
273 #[inline(never)]
274 #[cold]
275 fn check_cel_programs_with(&self, val: Self::Target) -> Result<(), Vec<CelError>> {
276 if self.programs.is_empty() {
277 Ok(())
278 } else {
279 test_programs(&self.programs, val)
280 }
281 }
282
283 #[cfg(feature = "cel")]
284 #[inline(never)]
285 #[cold]
286 #[doc(hidden)]
287 fn __check_cel_programs(&self) -> Result<(), Vec<CelError>> {
288 <Self as Validator<T>>::check_cel_programs_with(self, Self::Target::default())
289 }
290
291 #[doc(hidden)]
292 #[inline(never)]
293 #[cold]
294 fn __cel_rules(&self) -> Vec<CelRule> {
295 self.programs
296 .iter()
297 .map(|p| p.rule().clone())
298 .collect()
299 }
300
301 #[inline(never)]
302 #[cold]
303 fn check_consistency(&self) -> Result<(), Vec<ConsistencyError>> {
304 let mut errors = Vec::new();
305
306 #[cfg(feature = "cel")]
307 if let Err(e) = <Self as Validator<T>>::__check_cel_programs(self) {
308 errors.extend(e.into_iter().map(ConsistencyError::from));
309 }
310
311 if errors.is_empty() {
312 Ok(())
313 } else {
314 Err(errors)
315 }
316 }
317
318 fn execute_validation(
319 &self,
320 ctx: &mut ValidationCtx,
321 val: Option<&Self::Target>,
322 ) -> ValidationResult {
323 let mut is_valid = IsValid::Yes;
324
325 if let Some(val) = val {
326 #[cfg(feature = "cel")]
327 if !self.programs.is_empty() {
328 let cel_ctx = ProgramsExecutionCtx {
329 programs: &self.programs,
330 value: val.clone(),
331 ctx,
332 };
333
334 is_valid &= cel_ctx.execute_programs()?;
335 }
336 }
337
338 Ok(is_valid)
339 }
340
341 #[inline(never)]
342 #[cold]
343 fn schema(&self) -> Option<ValidatorSchema> {
344 Some(ValidatorSchema {
345 schema: self.clone().into(),
346 cel_rules: <Self as Validator<T>>::__cel_rules(self),
347 imports: vec!["buf/validate/validate.proto".into()],
348 })
349 }
350}
351
352impl From<CelValidator> for ProtoOption {
353 #[inline(never)]
354 #[cold]
355 fn from(value: CelValidator) -> Self {
356 let mut rules = OptionMessageBuilder::new();
357
358 rules.add_cel_options(value.programs);
359
360 Self {
361 name: "(buf.validate.message)".into(),
362 value: OptionValue::Message(rules.into()),
363 }
364 }
365}