protovalidate_buffa/lib.rs
1//! Runtime companion for `protoc-gen-protovalidate-buffa`.
2//!
3//! Provides the [`Validate`] trait, the [`ValidationError`] /
4//! [`Violation`] / [`FieldPath`] types returned from generated
5//! `validate()` methods, the [`cel`] module of thin helpers that
6//! compile-time-expanded CEL rules call into (scalar widening,
7//! Duration/Timestamp converters, `now`), and the [`rules`] module
8//! of pure-Rust helpers used by generated code (UUID / ULID / IP /
9//! URI / hostname checks and friends, mostly thin wrappers over
10//! `uuid`, `ulid`, `ipnet`, and `fluent-uri`).
11//!
12//! There is **no CEL interpreter at runtime**: the paired
13//! `protoc-gen-protovalidate-buffa` plugin transpiles every CEL rule
14//! to native Rust at codegen time. Generated `validate()` methods are
15//! direct field-access checks without per-call dynamic CEL `Value`
16//! materialization. Rules that need scratch state, such as repeated-value
17//! uniqueness, allocate it directly.
18//!
19//! [`ValidationError`] carries three orthogonal signals:
20//!
21//! - `violations`: list of per-field rule failures (the common case).
22//! - `compile_error`: non-empty when the codegen plugin detected a
23//! schema-level mismatch (rule type / field type, duplicate / unknown
24//! fields in `message.oneof`, CEL referencing a non-existent field).
25//! - `runtime_error`: non-empty when a rule's precondition could not be
26//! evaluated (e.g. `bytes.pattern` on non-UTF-8 input, or a CEL rule
27//! that compiled-time analysis flagged as always-runtime-error such as
28//! `dyn(this).<unknown_field>`).
29//!
30//! The full upstream `protovalidate-conformance` suite (2872 cases,
31//! covering proto2, proto3, and editions 2023) passes against code
32//! emitted by the paired plugin.
33
34#[doc(hidden)]
35pub mod __private;
36pub mod cel;
37mod error;
38#[cfg(feature = "protos")]
39mod error_proto;
40pub mod rules;
41
42#[cfg(feature = "connect")]
43mod connect;
44// Re-export `regex` so generated patterns (`::protovalidate_buffa::regex::Regex`)
45// resolve without each downstream crate having to add a direct `regex` dep.
46// `buffa` is re-exported for convenience but generated code uses the
47// `::buffa::` path directly; downstream crates already depend on buffa for
48// their message types.
49pub use buffa;
50/// Date/time types and traits used by generated CEL timestamp expressions.
51pub use chrono;
52/// IANA timezone database, re-exported so generated code can reference
53/// `::protovalidate_buffa::chrono_tz::Tz` when a CEL rule uses the
54/// timezone-argument form of a timestamp accessor
55/// (`t.getHours("America/New_York")`). Only exported when the `tz`
56/// feature is enabled — rules without tz args don't need this dep.
57#[cfg(feature = "tz")]
58pub use chrono_tz;
59#[cfg(feature = "connect")]
60pub use connect::{DecodeViolationsError, decode_violations};
61pub use error::{FieldPath, FieldPathElement, FieldType, Subscript, ValidationError, Violation};
62/// `#[connect_impl]` — attribute macro applied to a Connect service `impl`
63/// block that validates the borrowed request view at the top of every handler
64/// method, without converting it to an owned message. Requires `Validate` on
65/// the generated view type.
66/// Guarantees protovalidate runs for every RPC without relying on per-handler
67/// discipline.
68///
69/// Only exported when the `connect` feature is enabled (the default), since
70/// the emitted code calls [`ValidationError::into_connect_error`].
71#[cfg(feature = "connect")]
72pub use protovalidate_buffa_macros::connect_impl;
73/// Canonical generated `buf.validate` messages, including `Violations`.
74///
75/// Requires the `protos` feature (also enabled by `connect`). These are the
76/// types from `protovalidate-buffa-protos`; callers need no duplicate codegen.
77#[cfg(feature = "protos")]
78#[doc(no_inline)]
79pub use protovalidate_buffa_protos::buf::validate as proto;
80pub use regex;
81
82pub trait Validate {
83 /// Runs every rule attached to this message (and any nested messages),
84 /// collecting violations rather than short-circuiting on the first.
85 ///
86 /// # Errors
87 ///
88 /// Returns a [`ValidationError`] containing one or more [`Violation`]s
89 /// when any rule fails, or compilation/evaluation diagnostics when a rule
90 /// cannot run. For RPC requests, `ValidationError::into_connect_error`
91 /// (the `connect` feature) maps violations to `invalid_argument` and
92 /// validator defects to `internal`.
93 fn validate(&self) -> Result<(), ValidationError>;
94}
95
96#[macro_export]
97macro_rules! field_path {
98 ( $( $part:expr ),* $(,)? ) => {{
99 let mut elements = ::std::vec::Vec::new();
100 $(
101 elements.push($crate::FieldPathElement {
102 field_number: None,
103 field_name: Some(::std::borrow::Cow::Borrowed($part)),
104 field_type: None,
105 key_type: None,
106 value_type: None,
107 subscript: None,
108 });
109 )*
110 $crate::FieldPath { elements }
111 }};
112}