prost_protovalidate/config.rs
1use prost_reflect::{
2 DynamicMessage, FieldDescriptor, MessageDescriptor, OneofDescriptor, ReflectMessage,
3};
4use prost_types::Timestamp;
5use std::sync::Arc;
6
7/// Options for configuring the `Validator` at construction time.
8#[non_exhaustive]
9pub enum ValidatorOption {
10 /// Stop validation on the first violation instead of collecting all.
11 FailFast,
12
13 /// Disable lazy compilation: all known message types must be
14 /// pre-registered, and unknown types will produce a compilation error.
15 DisableLazy,
16
17 /// Override the function used to populate `now` in timestamp-based rules/CEL.
18 NowFn(Arc<dyn Fn() -> Timestamp + Send + Sync>),
19
20 /// Additional encoded `FileDescriptorSet` payloads used to resolve
21 /// custom/proprietary rule extensions at runtime.
22 AdditionalDescriptorSetBytes(Vec<u8>),
23
24 /// Preload evaluators for these descriptors at validator construction time.
25 /// Useful with `DisableLazy` to allow a fixed set of message types.
26 MessageDescriptors(Vec<MessageDescriptor>),
27
28 /// Allow unknown fields in constraint messages instead of producing a
29 /// compilation error. Useful when working with newer constraint protos
30 /// that contain fields not yet recognized by this library.
31 AllowUnknownFields,
32}
33
34/// Options for configuring a single `Validator::validate_with` call.
35#[non_exhaustive]
36pub enum ValidationOption {
37 /// Stop validation on the first violation instead of collecting all.
38 FailFast,
39 /// Override the filter for this validation call.
40 Filter(Arc<dyn Filter>),
41 /// Override the function used to populate `now` in timestamp-based rules/CEL.
42 NowFn(Arc<dyn Fn() -> Timestamp + Send + Sync>),
43}
44
45/// Controls which fields/messages are validated.
46pub trait Filter: Send + Sync {
47 /// Returns true if the given message should be validated.
48 fn should_validate(&self, message: &DynamicMessage, descriptor: &MessageDescriptor) -> bool;
49
50 /// Returns true if the given field should be validated.
51 /// Defaults to message-level filtering for compatibility.
52 fn should_validate_field(&self, message: &DynamicMessage, _field: &FieldDescriptor) -> bool {
53 let descriptor = message.descriptor();
54 self.should_validate(message, &descriptor)
55 }
56
57 /// Returns true if the given oneof should be validated.
58 /// Defaults to message-level filtering for compatibility.
59 fn should_validate_oneof(&self, message: &DynamicMessage, _oneof: &OneofDescriptor) -> bool {
60 let descriptor = message.descriptor();
61 self.should_validate(message, &descriptor)
62 }
63}
64
65/// A filter that always validates everything.
66pub(crate) struct NopFilter;
67
68impl Filter for NopFilter {
69 fn should_validate(&self, _message: &DynamicMessage, _descriptor: &MessageDescriptor) -> bool {
70 true
71 }
72}
73
74/// Runtime configuration passed to evaluators during validation.
75pub(crate) struct ValidationConfig {
76 pub fail_fast: bool,
77 pub filter: Arc<dyn Filter>,
78 pub now_fn: Arc<dyn Fn() -> Timestamp + Send + Sync>,
79}
80
81impl Default for ValidationConfig {
82 fn default() -> Self {
83 Self {
84 fail_fast: false,
85 filter: Arc::new(NopFilter),
86 now_fn: Arc::new(|| {
87 let now = std::time::SystemTime::now()
88 .duration_since(std::time::UNIX_EPOCH)
89 .unwrap_or_default();
90 // Both casts are semantically safe:
91 // - as_secs() since UNIX_EPOCH fits in i64 for billions of years
92 // - subsec_nanos() is always < 1_000_000_000 which fits in i32
93 #[allow(clippy::cast_possible_wrap)]
94 Timestamp {
95 seconds: now.as_secs() as i64,
96 nanos: now.subsec_nanos() as i32,
97 }
98 }),
99 }
100 }
101}