Skip to main content

miden_protobuf/
message.rs

1use alloc::boxed::Box;
2use alloc::format;
3use core::error::Error;
4
5use crate::ConversionError;
6
7/// Extracts an infallible result without introducing a potential panic.
8///
9/// ```
10/// use core::convert::Infallible;
11///
12/// use miden_protobuf::unwrap_infallible;
13///
14/// assert_eq!(unwrap_infallible(Ok::<_, Infallible>(42)), 42);
15/// ```
16///
17/// Fallible results are rejected at compile time:
18///
19/// ```compile_fail,E0308
20/// use miden_protobuf::unwrap_infallible;
21///
22/// unwrap_infallible("42".parse::<u32>());
23/// ```
24pub fn unwrap_infallible<T>(result: Result<T, core::convert::Infallible>) -> T {
25    match result {
26        Ok(value) => value,
27        Err(impossible) => match impossible {},
28    }
29}
30
31/// Decodes a wire message or oneof without constructing its verified domain counterpart.
32///
33/// Derived implementations produce schema-shaped records. Atomic messages can select an existing
34/// deserialized type instead, using its `TryFrom` implementation. Such adapters should check the
35/// representation only, leaving application invariants to [`Verify`] or [`VerifyWith`].
36/// Use [`DecodeMessageExt`] to combine field decoding with an explicit construction capability.
37pub trait DecodeMessage: Sized {
38    type Decoded: TryFrom<Self, Error = ConversionError>;
39
40    fn decode_fields(self) -> Result<Self::Decoded, ConversionError> {
41        self.try_into()
42    }
43}
44
45/// The decoded representation of a **wire message or oneof** `P`, not its verified domain
46/// counterpart.
47pub type Decoded<P> = <P as DecodeMessage>::Decoded;
48
49/// Combines field decoding with an explicitly selected domain construction capability.
50///
51/// Implemented for every [`DecodeMessage`], including oneofs and handwritten adapters. Each
52/// method requires only its corresponding capability on the decoded representation. These methods
53/// consume an already parsed wire message; they do not decode Protobuf bytes.
54///
55/// All methods return [`ConversionError`] with a stage prefix: `failed to decode`,
56/// `failed to verify`, or `failed to build unchecked`. The original error, including any field
57/// path, is preserved in the source chain. Stage labels are separate from wire paths. Call
58/// [`DecodeMessage::decode_fields`] and the construction method separately when typed domain
59/// errors are needed directly.
60pub trait DecodeMessageExt: DecodeMessage {
61    /// Decodes fields, then checks domain invariants using [`Verify::verify`].
62    ///
63    /// ```
64    /// use miden_protobuf::{ConversionError, DecodeMessageExt, Verify};
65    ///
66    /// fn decode<P>(message: P) -> Result<<P::Decoded as Verify>::Verified, ConversionError>
67    /// where
68    ///     P: DecodeMessageExt,
69    ///     P::Decoded: Verify,
70    /// {
71    ///     message.decode_and_verify()
72    /// }
73    /// ```
74    fn decode_and_verify(self) -> Result<<Self::Decoded as Verify>::Verified, ConversionError>
75    where
76        Self::Decoded: Verify,
77    {
78        let decoded = self.decode_fields().map_err(|error| stage_error("decode", error))?;
79        decoded.verify().map_err(|error| stage_error("verify", error))
80    }
81
82    /// Decodes fields, then verifies with borrowed or owned caller-supplied context.
83    ///
84    /// The context must satisfy the trust requirements of the decoded type's
85    /// [`VerifyWith`] implementation.
86    ///
87    /// ```
88    /// use miden_protobuf::{ConversionError, DecodeMessageExt, VerifyWith};
89    ///
90    /// fn decode<P, C>(
91    ///     message: P,
92    ///     context: C,
93    /// ) -> Result<<P::Decoded as VerifyWith<C>>::Verified, ConversionError>
94    /// where
95    ///     P: DecodeMessageExt,
96    ///     P::Decoded: VerifyWith<C>,
97    /// {
98    ///     message.decode_and_verify_with(context)
99    /// }
100    /// ```
101    fn decode_and_verify_with<C>(
102        self,
103        context: C,
104    ) -> Result<<Self::Decoded as VerifyWith<C>>::Verified, ConversionError>
105    where
106        Self::Decoded: VerifyWith<C>,
107    {
108        let decoded = self.decode_fields().map_err(|error| stage_error("decode", error))?;
109        decoded.verify_with(context).map_err(|error| stage_error("verify", error))
110    }
111
112    /// Decodes fields, then constructs with [`BuildUnchecked::build_unchecked`].
113    ///
114    /// Structural decoding checks still run, and construction can still fail.
115    ///
116    /// # Warning
117    ///
118    /// The output is not guaranteed to be verified. Callers must ensure the invariants documented
119    /// by the decoded type's [`BuildUnchecked`] implementation, including any nested checks it
120    /// skips.
121    ///
122    /// ```
123    /// use miden_protobuf::{BuildUnchecked, ConversionError, DecodeMessageExt};
124    ///
125    /// fn decode<P>(message: P) -> Result<<P::Decoded as BuildUnchecked>::Output, ConversionError>
126    /// where
127    ///     P: DecodeMessageExt,
128    ///     P::Decoded: BuildUnchecked,
129    /// {
130    ///     message.decode_and_build_unchecked()
131    /// }
132    /// ```
133    fn decode_and_build_unchecked(
134        self,
135    ) -> Result<<Self::Decoded as BuildUnchecked>::Output, ConversionError>
136    where
137        Self::Decoded: BuildUnchecked,
138    {
139        let decoded = self.decode_fields().map_err(|error| stage_error("decode", error))?;
140        decoded.build_unchecked().map_err(|error| stage_error("build unchecked", error))
141    }
142}
143
144impl<P: DecodeMessage> DecodeMessageExt for P {}
145
146fn stage_error(stage: &'static str, error: impl Error + Send + Sync + 'static) -> ConversionError {
147    ConversionError::with_source(format!("failed to {stage}: {error}"), error)
148}
149
150/// Checks domain invariants and constructs the verified type using ordinary Rust.
151///
152/// Verification errors belong to the domain. Cross-field checks need not correspond to a
153/// single wire field.
154/// Types that require external context can implement [`VerifyWith`] instead.
155/// Boxes delegate to the contained verifier, preserving its error type and boxing the output.
156/// Generated decoded records retain collections in [`crate::OptionalField`],
157/// [`crate::RepeatedField`], or [`crate::MapField`]. Calling `verify()` on these fields verifies
158/// their elements with the generated field name and index or key context. These helpers return
159/// [`ConversionError`], preserving the original source, and stop at the first error.
160/// For element verifiers whose error is [`core::convert::Infallible`], the wrappers also provide
161/// `verify_infallible()`, returning the verified collection directly.
162/// Collection-wide invariants remain the responsibility of the containing verifier. Use the
163/// wrappers' `map()` or `try_map()` methods for explicit element conversions; `try_map()` retains
164/// field, index, and key context. Use `into_inner()` for custom collection-wide processing.
165pub trait Verify: Sized {
166    type Verified;
167    type Error: Error + Send + Sync + 'static;
168
169    fn verify(self) -> Result<Self::Verified, Self::Error>;
170}
171
172/// Checks domain invariants using caller-supplied context and constructs the verified type.
173///
174/// Context can be borrowed, such as a trusted parent header, or owned, such as a security level.
175/// Use a named context struct when verification needs several inputs. Implementations must
176/// document any trust requirements on the context.
177///
178/// This capability is independent of [`Verify`]: implementing it does not provide context-free
179/// verification. Implementations are handwritten; decoding does not invoke verification.
180/// Boxes delegate to the contained verifier without cloning the context or changing its error.
181/// The collection field wrappers also implement this trait, retaining field, index, and key
182/// context as with [`Verify`]. For vectors and maps, context is cloned once per element; pass
183/// `&context` to share a context without cloning its contents.
184pub trait VerifyWith<C>: Sized {
185    type Verified;
186    type Error: Error + Send + Sync + 'static;
187
188    fn verify_with(self, context: C) -> Result<Self::Verified, Self::Error>;
189}
190
191/// Constructs a domain object from decoded fields while skipping selected verification checks.
192///
193/// This is an explicit, handwritten capability, independent of [`Verify`] and [`VerifyWith`].
194/// Implement it only where the domain API supports unchecked construction. Construction can still
195/// fail on remaining checks or conversions; use [`core::convert::Infallible`] when it cannot fail.
196/// Collection field wrappers implement this capability when their elements do, preserving
197/// presence, order, duplicates, and keys. They stop at the first construction error, retaining
198/// its field, index, or key context. They do not invoke [`Verify`] or check collection invariants.
199///
200/// # Warning
201///
202/// Implementations must document precisely which checks are skipped, including nested checks,
203/// and which invariants callers must ensure. The output is not guaranteed to be verified. This
204/// operation must not bypass wire decoding checks or silently discard verification errors.
205/// Skipping domain validation alone does not make this a Rust `unsafe` operation.
206pub trait BuildUnchecked: Sized {
207    type Output;
208    type Error: Error + Send + Sync + 'static;
209
210    fn build_unchecked(self) -> Result<Self::Output, Self::Error>;
211}
212
213impl<S: Verify> Verify for Box<S> {
214    type Verified = Box<S::Verified>;
215    type Error = S::Error;
216
217    fn verify(self) -> Result<Self::Verified, Self::Error> {
218        (*self).verify().map(Box::new)
219    }
220}
221
222impl<S: VerifyWith<C>, C> VerifyWith<C> for Box<S> {
223    type Verified = Box<S::Verified>;
224    type Error = S::Error;
225
226    fn verify_with(self, context: C) -> Result<Self::Verified, Self::Error> {
227        (*self).verify_with(context).map(Box::new)
228    }
229}
230
231/// Preserves the box and delegates construction; the caller must ensure the invariants
232/// documented by `S`'s `BuildUnchecked` implementation.
233impl<S: BuildUnchecked> BuildUnchecked for Box<S> {
234    type Output = Box<S::Output>;
235    type Error = S::Error;
236
237    fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
238        (*self).build_unchecked().map(Box::new)
239    }
240}