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}