Skip to main content

prost_protovalidate/
config.rs

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