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 that backs message-level
6//! and field-level CEL rules, and the [`rules`] module of pure-Rust
7//! helpers used by generated code (UUID / ULID / IP / URI / hostname
8//! checks and friends, mostly thin wrappers over `uuid`, `ulid`,
9//! `ipnet`, and `fluent-uri`).
10//!
11//! [`ValidationError`] carries three orthogonal signals:
12//!
13//! - `violations`: list of per-field rule failures (the common case).
14//! - `compile_error`: non-empty when the codegen plugin detected a
15//! schema-level mismatch (rule type / field type, duplicate / unknown
16//! fields in `message.oneof`, CEL referencing a non-existent field).
17//! - `runtime_error`: non-empty when a rule's precondition could not be
18//! evaluated (e.g. `bytes.pattern` on non-UTF-8 input, CEL type
19//! mismatch).
20//!
21//! The full upstream `protovalidate-conformance` suite (2872 cases,
22//! covering proto2, proto3, and editions 2023) passes against code
23//! emitted by the paired plugin.
24
25pub mod cel;
26mod error;
27pub mod rules;
28
29#[cfg(feature = "connect")]
30mod connect;
31
32// Re-export external crates referenced by plugin-generated code so that
33// downstream crates only need to depend on `protovalidate-buffa` and not on
34// `regex` / `cel-interpreter` / `buffa` directly.
35pub use buffa;
36pub use cel_interpreter;
37pub use error::{FieldPath, FieldPathElement, FieldType, Subscript, ValidationError, Violation};
38/// `#[connect_impl]` — attribute macro applied to a Connect service `impl`
39/// block that inserts `req.validate()?` at the top of every handler method.
40/// Guarantees protovalidate runs for every RPC without relying on per-handler
41/// discipline.
42pub use protovalidate_buffa_macros::connect_impl;
43pub use regex;
44
45pub trait Validate {
46 /// Runs every rule attached to this message (and any nested messages),
47 /// collecting violations rather than short-circuiting on the first.
48 ///
49 /// # Errors
50 ///
51 /// Returns a [`ValidationError`] containing one or more [`Violation`]s
52 /// when any rule fails. Callers typically map this to
53 /// `ConnectError::invalid_argument` via
54 /// [`ValidationError::into_connect_error`] (requires the `connect` feature).
55 fn validate(&self) -> Result<(), ValidationError>;
56}
57
58#[macro_export]
59macro_rules! field_path {
60 ( $( $part:expr ),* $(,)? ) => {{
61 let mut elements = ::std::vec::Vec::new();
62 $(
63 elements.push($crate::FieldPathElement {
64 field_number: None,
65 field_name: Some(::std::borrow::Cow::Borrowed($part)),
66 field_type: None,
67 key_type: None,
68 value_type: None,
69 subscript: None,
70 });
71 )*
72 $crate::FieldPath { elements }
73 }};
74}