fidl_next_protocol/
flexible_result.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::fmt;
6use core::marker::PhantomData;
7use core::mem::{ManuallyDrop, MaybeUninit};
8
9use fidl_next_codec::{
10    Chunk, Constrained, Decode, DecodeError, Decoder, Encode, EncodeError, Encoder, FromWire,
11    FromWireRef, IntoNatural, RawWireUnion, Slot, Unconstrained, Wire, WireResult, munge,
12};
13
14use crate::{FrameworkError, WireFrameworkError};
15
16/// A flexible FIDL result.
17#[derive(Clone, Debug)]
18pub enum FlexibleResult<T, E> {
19    /// The value of the flexible call when successful.
20    Ok(T),
21    /// The error returned from a successful flexible call.
22    Err(E),
23    /// The error indicating that the flexible call failed.
24    FrameworkErr(FrameworkError),
25}
26
27impl<T, E> FlexibleResult<T, E> {
28    /// Returns whether the flexible result is `Ok`.
29    pub fn is_ok(&self) -> bool {
30        matches!(self, Self::Ok(_))
31    }
32
33    /// Returns whether the flexible result if `Err`.
34    pub fn is_err(&self) -> bool {
35        matches!(self, Self::Err(_))
36    }
37
38    /// Returns whether the flexible result is `FrameworkErr`.
39    pub fn is_framework_err(&self) -> bool {
40        matches!(self, Self::FrameworkErr(_))
41    }
42
43    /// Returns the `Ok` value of the result, if any.
44    pub fn ok(self) -> Option<T> {
45        if let Self::Ok(value) = self { Some(value) } else { None }
46    }
47
48    /// Returns the `Err` value of the result, if any.
49    pub fn err(self) -> Option<E> {
50        if let Self::Err(error) = self { Some(error) } else { None }
51    }
52
53    /// Returns the `FrameworkErr` value of the result, if any.
54    pub fn framework_err(self) -> Option<FrameworkError> {
55        if let Self::FrameworkErr(error) = self { Some(error) } else { None }
56    }
57
58    /// Returns the contained `Ok` value.
59    ///
60    /// Panics if the result was not `Ok`.
61    pub fn unwrap(self) -> T {
62        self.ok().unwrap()
63    }
64
65    /// Returns the contained `Err` value.
66    ///
67    /// Panics if the result was not `Err`.
68    pub fn unwrap_err(self) -> E {
69        self.err().unwrap()
70    }
71
72    /// Returns the contained `FrameworkErr` value.
73    ///
74    /// Panics if the result was not `FrameworkErr`.
75    pub fn unwrap_framework_err(self) -> FrameworkError {
76        self.framework_err().unwrap()
77    }
78
79    /// Converts from `FlexibleResult<T, E>` to `FlexibleResult<&T, &E>`.
80    pub fn as_ref(&self) -> FlexibleResult<&T, &E> {
81        match self {
82            Self::Ok(value) => FlexibleResult::Ok(value),
83            Self::Err(error) => FlexibleResult::Err(error),
84            Self::FrameworkErr(framework_error) => FlexibleResult::FrameworkErr(*framework_error),
85        }
86    }
87}
88
89/// A flexible FIDL result.
90#[repr(transparent)]
91pub struct WireFlexibleResult<'de, T, E> {
92    raw: RawWireUnion,
93    _phantom: PhantomData<(&'de mut [Chunk], T, E)>,
94}
95
96impl<T, E> Drop for WireFlexibleResult<'_, T, E> {
97    fn drop(&mut self) {
98        match self.raw.ordinal() {
99            ORD_OK => {
100                let _ = unsafe { self.raw.get().read_unchecked::<T>() };
101            }
102            ORD_ERR => {
103                let _ = unsafe { self.raw.get().read_unchecked::<E>() };
104            }
105            ORD_FRAMEWORK_ERR => {
106                let _ = unsafe { self.raw.get().read_unchecked::<WireFrameworkError>() };
107            }
108            _ => unsafe { ::core::hint::unreachable_unchecked() },
109        }
110    }
111}
112
113unsafe impl<T: Wire, E: Wire> Wire for WireFlexibleResult<'static, T, E> {
114    type Owned<'de> = WireFlexibleResult<'de, T::Owned<'de>, E::Owned<'de>>;
115
116    #[inline]
117    fn zero_padding(out: &mut MaybeUninit<Self>) {
118        munge!(let Self { raw, _phantom: _ } = out);
119        RawWireUnion::zero_padding(raw);
120    }
121}
122impl<T, E> Unconstrained for WireFlexibleResult<'_, T, E>
123where
124    T: Constrained<Constraint = ()>,
125    E: Constrained<Constraint = ()>,
126{
127}
128
129const ORD_OK: u64 = 1;
130const ORD_ERR: u64 = 2;
131const ORD_FRAMEWORK_ERR: u64 = 3;
132
133impl<'de, T, E> WireFlexibleResult<'de, T, E> {
134    /// Returns whether the flexible result is `Ok`.
135    pub fn is_ok(&self) -> bool {
136        self.raw.ordinal() == ORD_OK
137    }
138
139    /// Returns whether the flexible result if `Err`.
140    pub fn is_err(&self) -> bool {
141        self.raw.ordinal() == ORD_ERR
142    }
143
144    /// Returns whether the flexible result is `FrameworkErr`.
145    pub fn is_framework_err(&self) -> bool {
146        self.raw.ordinal() == ORD_FRAMEWORK_ERR
147    }
148
149    /// Returns the `Ok` value of the result, if any.
150    pub fn ok(&self) -> Option<&T> {
151        self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
152    }
153
154    /// Returns the `Err` value of the result, if any.
155    pub fn err(&self) -> Option<&E> {
156        self.is_err().then(|| unsafe { self.raw.get().deref_unchecked() })
157    }
158
159    /// Returns the `FrameworkErr` value of the result, if any.
160    pub fn framework_err(&self) -> Option<FrameworkError> {
161        self.is_framework_err()
162            .then(|| unsafe { (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into() })
163    }
164
165    /// Returns the contained `Ok` value.
166    ///
167    /// Panics if the result was not `Ok`.
168    pub fn unwrap(&self) -> &T {
169        self.ok().unwrap()
170    }
171
172    /// Returns the contained `Err` value.
173    ///
174    /// Panics if the result was not `Err`.
175    pub fn unwrap_err(&self) -> &E {
176        self.err().unwrap()
177    }
178
179    /// Returns the contained `FrameworkErr` value.
180    ///
181    /// Panics if the result was not `FrameworkErr`.
182    pub fn unwrap_framework_err(&self) -> FrameworkError {
183        self.framework_err().unwrap()
184    }
185
186    /// Returns a `FlexibleResult` of a reference to the value or framework error.
187    pub fn as_ref(&self) -> FlexibleResult<&T, &E> {
188        match self.raw.ordinal() {
189            ORD_OK => unsafe { FlexibleResult::Ok(self.raw.get().deref_unchecked()) },
190            ORD_ERR => unsafe { FlexibleResult::Err(self.raw.get().deref_unchecked()) },
191            ORD_FRAMEWORK_ERR => unsafe {
192                FlexibleResult::FrameworkErr(
193                    (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into(),
194                )
195            },
196            _ => unsafe { ::core::hint::unreachable_unchecked() },
197        }
198    }
199
200    /// Returns a `Result` of the `Ok` value and a potential `FrameworkError`.
201    pub fn as_response(&self) -> Result<&WireResult<'_, T, E>, FrameworkError> {
202        match self.raw.ordinal() {
203            ORD_OK | ORD_ERR => unsafe {
204                Ok(&*(self as *const Self as *const WireResult<'_, T, E>))
205            },
206            ORD_FRAMEWORK_ERR => unsafe {
207                Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
208            },
209            _ => unsafe { ::core::hint::unreachable_unchecked() },
210        }
211    }
212
213    /// Returns a nested `Result` of the `Ok` and `Err` values, and a potential `FrameworkError`.
214    pub fn as_result(&self) -> Result<Result<&T, &E>, FrameworkError> {
215        match self.raw.ordinal() {
216            ORD_OK => unsafe { Ok(Ok(self.raw.get().deref_unchecked())) },
217            ORD_ERR => unsafe { Ok(Err(self.raw.get().deref_unchecked())) },
218            ORD_FRAMEWORK_ERR => unsafe {
219                Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
220            },
221            _ => unsafe { ::core::hint::unreachable_unchecked() },
222        }
223    }
224
225    /// Returns a `FlexibleResult` of a value or framework error.
226    pub fn to_flexible_result(self) -> FlexibleResult<T, E> {
227        let this = ManuallyDrop::new(self);
228        match this.raw.ordinal() {
229            ORD_OK => unsafe { FlexibleResult::Ok(this.raw.get().read_unchecked()) },
230            ORD_ERR => unsafe { FlexibleResult::Err(this.raw.get().read_unchecked()) },
231            ORD_FRAMEWORK_ERR => unsafe {
232                FlexibleResult::FrameworkErr(
233                    this.raw.get().read_unchecked::<WireFrameworkError>().into(),
234                )
235            },
236            _ => unsafe { ::core::hint::unreachable_unchecked() },
237        }
238    }
239}
240
241impl<T: Clone, E: Clone> Clone for WireFlexibleResult<'_, T, E> {
242    fn clone(&self) -> Self {
243        Self {
244            raw: match self.raw.ordinal() {
245                ORD_OK => unsafe { self.raw.clone_inline_unchecked::<T>() },
246                ORD_ERR => unsafe { self.raw.clone_inline_unchecked::<E>() },
247                ORD_FRAMEWORK_ERR => unsafe {
248                    self.raw.clone_inline_unchecked::<WireFrameworkError>()
249                },
250                _ => unsafe { ::core::hint::unreachable_unchecked() },
251            },
252            _phantom: PhantomData,
253        }
254    }
255}
256
257impl<T, E> fmt::Debug for WireFlexibleResult<'_, T, E>
258where
259    T: fmt::Debug,
260    E: fmt::Debug,
261{
262    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263        self.as_ref().fmt(f)
264    }
265}
266
267unsafe impl<D, T, E> Decode<D> for WireFlexibleResult<'static, T, E>
268where
269    D: Decoder + ?Sized,
270    T: Decode<D> + Constrained<Constraint = ()>,
271    E: Decode<D> + Constrained<Constraint = ()>,
272{
273    fn decode(slot: Slot<'_, Self>, decoder: &mut D, _: ()) -> Result<(), DecodeError> {
274        munge!(let Self { mut raw, _phantom: _ } = slot);
275
276        match RawWireUnion::encoded_ordinal(raw.as_mut()) {
277            ORD_OK => RawWireUnion::decode_as::<D, T>(raw, decoder, ())?,
278            ORD_ERR => RawWireUnion::decode_as::<D, E>(raw, decoder, ())?,
279            ORD_FRAMEWORK_ERR => {
280                RawWireUnion::decode_as::<D, WireFrameworkError>(raw, decoder, ())?
281            }
282            ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
283        }
284
285        Ok(())
286    }
287}
288
289unsafe impl<Enc, WT, T, WE, E> Encode<WireFlexibleResult<'static, WT, WE>, Enc>
290    for FlexibleResult<T, E>
291where
292    Enc: Encoder + ?Sized,
293    WT: Constrained<Constraint = ()> + Wire,
294    T: Encode<WT, Enc>,
295    WE: Constrained<Constraint = ()> + Wire,
296    E: Encode<WE, Enc>,
297{
298    fn encode(
299        self,
300        encoder: &mut Enc,
301        out: &mut MaybeUninit<WireFlexibleResult<'static, WT, WE>>,
302        _: (),
303    ) -> Result<(), EncodeError> {
304        munge!(let WireFlexibleResult { raw, _phantom: _ } = out);
305
306        match self {
307            Self::Ok(value) => RawWireUnion::encode_as::<Enc, WT>(value, ORD_OK, encoder, raw, ())?,
308            Self::Err(error) => {
309                RawWireUnion::encode_as::<Enc, WE>(error, ORD_ERR, encoder, raw, ())?
310            }
311            Self::FrameworkErr(error) => RawWireUnion::encode_as::<Enc, WireFrameworkError>(
312                error,
313                ORD_FRAMEWORK_ERR,
314                encoder,
315                raw,
316                (),
317            )?,
318        }
319
320        Ok(())
321    }
322}
323
324unsafe impl<'a, Enc, WT, T, WE, E> Encode<WireFlexibleResult<'static, WT, WE>, Enc>
325    for &'a FlexibleResult<T, E>
326where
327    Enc: Encoder + ?Sized,
328    WT: Constrained<Constraint = ()> + Wire,
329    &'a T: Encode<WT, Enc>,
330    WE: Constrained<Constraint = ()> + Wire,
331    &'a E: Encode<WE, Enc>,
332{
333    fn encode(
334        self,
335        encoder: &mut Enc,
336        out: &mut MaybeUninit<WireFlexibleResult<'static, WT, WE>>,
337        _: (),
338    ) -> Result<(), EncodeError> {
339        self.as_ref().encode(encoder, out, ())
340    }
341}
342
343impl<T, WT, E, WE> FromWire<WireFlexibleResult<'_, WT, WE>> for FlexibleResult<T, E>
344where
345    T: FromWire<WT>,
346    E: FromWire<WE>,
347{
348    fn from_wire(wire: WireFlexibleResult<'_, WT, WE>) -> Self {
349        match wire.to_flexible_result() {
350            FlexibleResult::Ok(value) => Self::Ok(T::from_wire(value)),
351            FlexibleResult::Err(error) => Self::Err(E::from_wire(error)),
352            FlexibleResult::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
353        }
354    }
355}
356
357impl<T: IntoNatural, E: IntoNatural> IntoNatural for WireFlexibleResult<'_, T, E> {
358    type Natural = FlexibleResult<T::Natural, E::Natural>;
359}
360
361impl<T, WT, E, WE> FromWireRef<WireFlexibleResult<'_, WT, WE>> for FlexibleResult<T, E>
362where
363    T: FromWireRef<WT>,
364    E: FromWireRef<WE>,
365{
366    fn from_wire_ref(wire: &WireFlexibleResult<'_, WT, WE>) -> Self {
367        match wire.as_ref() {
368            FlexibleResult::Ok(value) => Self::Ok(T::from_wire_ref(value)),
369            FlexibleResult::Err(error) => Self::Err(E::from_wire_ref(error)),
370            FlexibleResult::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
371        }
372    }
373}
374
375#[cfg(test)]
376mod tests {
377    use fidl_next_codec::{WireI32, chunks};
378
379    use super::{FlexibleResult, WireFlexibleResult};
380    use crate::FrameworkError;
381    use crate::testing::{assert_decoded, assert_encoded};
382
383    #[test]
384    fn encode_flexible_result() {
385        assert_encoded(
386            FlexibleResult::<(), i32>::Ok(()),
387            &chunks![
388                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
389                0x01, 0x00,
390            ],
391        );
392        assert_encoded(
393            FlexibleResult::<(), i32>::Err(0x12345678),
394            &chunks![
395                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
396                0x01, 0x00,
397            ],
398        );
399        assert_encoded(
400            FlexibleResult::<(), i32>::FrameworkErr(FrameworkError::UnknownMethod),
401            &chunks![
402                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
403                0x01, 0x00,
404            ],
405        );
406    }
407
408    #[test]
409    fn decode_flexible_result() {
410        assert_decoded::<WireFlexibleResult<'_, (), WireI32>>(
411            &mut chunks![
412                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413                0x01, 0x00,
414            ],
415            |x| assert!(matches!(x.as_ref(), FlexibleResult::Ok(()))),
416        );
417        assert_decoded::<WireFlexibleResult<'_, (), WireI32>>(
418            &mut chunks![
419                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
420                0x01, 0x00,
421            ],
422            |x| assert!(matches!(x.as_ref(), FlexibleResult::Err(WireI32(0x12345678)))),
423        );
424        assert_decoded::<WireFlexibleResult<'_, (), WireI32>>(
425            &mut chunks![
426                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
427                0x01, 0x00,
428            ],
429            |x| {
430                assert!(matches!(
431                    x.as_ref(),
432                    FlexibleResult::FrameworkErr(FrameworkError::UnknownMethod)
433                ))
434            },
435        );
436    }
437}