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    munge, Chunk, Decode, DecodeError, Decoder, Encodable, Encode, EncodeError, EncodeRef, Encoder,
11    FromWire, FromWireRef, RawWireUnion, Slot, Wire, WireResult,
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    /// Converts from `FlexibleResult<T, E>` to `FlexibleResult<&T, &E>`.
29    pub fn as_ref(&self) -> FlexibleResult<&T, &E> {
30        match self {
31            Self::Ok(value) => FlexibleResult::Ok(value),
32            Self::Err(error) => FlexibleResult::Err(error),
33            Self::FrameworkErr(framework_error) => FlexibleResult::FrameworkErr(*framework_error),
34        }
35    }
36}
37
38/// A flexible FIDL result.
39#[repr(transparent)]
40pub struct WireFlexibleResult<'de, T, E> {
41    raw: RawWireUnion,
42    _phantom: PhantomData<(&'de mut [Chunk], T, E)>,
43}
44
45impl<T, E> Drop for WireFlexibleResult<'_, T, E> {
46    fn drop(&mut self) {
47        match self.raw.ordinal() {
48            ORD_OK => {
49                let _ = unsafe { self.raw.get().read_unchecked::<T>() };
50            }
51            ORD_ERR => {
52                let _ = unsafe { self.raw.get().read_unchecked::<E>() };
53            }
54            ORD_FRAMEWORK_ERR => {
55                let _ = unsafe { self.raw.get().read_unchecked::<WireFrameworkError>() };
56            }
57            _ => unsafe { ::core::hint::unreachable_unchecked() },
58        }
59    }
60}
61
62unsafe impl<T: Wire, E: Wire> Wire for WireFlexibleResult<'static, T, E> {
63    type Decoded<'de> = WireFlexibleResult<'de, T::Decoded<'de>, E::Decoded<'de>>;
64
65    #[inline]
66    fn zero_padding(out: &mut MaybeUninit<Self>) {
67        munge!(let Self { raw, _phantom: _ } = out);
68        RawWireUnion::zero_padding(raw);
69    }
70}
71
72const ORD_OK: u64 = 1;
73const ORD_ERR: u64 = 2;
74const ORD_FRAMEWORK_ERR: u64 = 3;
75
76impl<'de, T, E> WireFlexibleResult<'de, T, E> {
77    /// Returns whether the flexible result is `Ok`.
78    pub fn is_ok(&self) -> bool {
79        self.raw.ordinal() == ORD_OK
80    }
81
82    /// Returns whether the flexible result if `Err`.
83    pub fn is_err(&self) -> bool {
84        self.raw.ordinal() == ORD_ERR
85    }
86
87    /// Returns whether the flexible result is `FrameworkErr`.
88    pub fn is_framework_err(&self) -> bool {
89        self.raw.ordinal() == ORD_FRAMEWORK_ERR
90    }
91
92    /// Returns the `Ok` value of the result, if any.
93    pub fn ok(&self) -> Option<&T> {
94        self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
95    }
96
97    /// Returns the `Err` value of the result, if any.
98    pub fn err(&self) -> Option<&E> {
99        self.is_err().then(|| unsafe { self.raw.get().deref_unchecked() })
100    }
101
102    /// Returns the `FrameworkErr` value of the result, if any.
103    pub fn framework_err(&self) -> Option<FrameworkError> {
104        self.is_framework_err()
105            .then(|| unsafe { (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into() })
106    }
107
108    /// Returns the contained `Ok` value.
109    ///
110    /// Panics if the result was not `Ok`.
111    pub fn unwrap(&self) -> &T {
112        self.ok().unwrap()
113    }
114
115    /// Returns the contained `Err` value.
116    ///
117    /// Panics if the result was not `Err`.
118    pub fn unwrap_err(&self) -> &E {
119        self.err().unwrap()
120    }
121
122    /// Returns the contained `FrameworkErr` value.
123    ///
124    /// Panics if the result was not `FrameworkErr`.
125    pub fn unwrap_framework_err(&self) -> FrameworkError {
126        self.framework_err().unwrap()
127    }
128
129    /// Returns a `FlexibleResult` of a reference to the value or framework error.
130    pub fn as_ref(&self) -> FlexibleResult<&T, &E> {
131        match self.raw.ordinal() {
132            ORD_OK => unsafe { FlexibleResult::Ok(self.raw.get().deref_unchecked()) },
133            ORD_ERR => unsafe { FlexibleResult::Err(self.raw.get().deref_unchecked()) },
134            ORD_FRAMEWORK_ERR => unsafe {
135                FlexibleResult::FrameworkErr(
136                    (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into(),
137                )
138            },
139            _ => unsafe { ::core::hint::unreachable_unchecked() },
140        }
141    }
142
143    /// Returns a `Result` of the `Ok` value and a potential `FrameworkError`.
144    pub fn as_response(&self) -> Result<&WireResult<'_, T, E>, FrameworkError> {
145        match self.raw.ordinal() {
146            ORD_OK | ORD_ERR => unsafe {
147                Ok(&*(self as *const Self as *const WireResult<'_, T, E>))
148            },
149            ORD_FRAMEWORK_ERR => unsafe {
150                Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
151            },
152            _ => unsafe { ::core::hint::unreachable_unchecked() },
153        }
154    }
155
156    /// Returns a nested `Result` of the `Ok` and `Err` values, and a potential `FrameworkError`.
157    pub fn as_result(&self) -> Result<Result<&T, &E>, FrameworkError> {
158        match self.raw.ordinal() {
159            ORD_OK => unsafe { Ok(Ok(self.raw.get().deref_unchecked())) },
160            ORD_ERR => unsafe { Ok(Err(self.raw.get().deref_unchecked())) },
161            ORD_FRAMEWORK_ERR => unsafe {
162                Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
163            },
164            _ => unsafe { ::core::hint::unreachable_unchecked() },
165        }
166    }
167
168    /// Returns a `FlexibleResult` of a value or framework error.
169    pub fn to_flexible_result(self) -> FlexibleResult<T, E> {
170        let this = ManuallyDrop::new(self);
171        match this.raw.ordinal() {
172            ORD_OK => unsafe { FlexibleResult::Ok(this.raw.get().read_unchecked()) },
173            ORD_ERR => unsafe { FlexibleResult::Err(this.raw.get().read_unchecked()) },
174            ORD_FRAMEWORK_ERR => unsafe {
175                FlexibleResult::FrameworkErr(
176                    this.raw.get().read_unchecked::<WireFrameworkError>().into(),
177                )
178            },
179            _ => unsafe { ::core::hint::unreachable_unchecked() },
180        }
181    }
182}
183
184impl<T: Clone, E: Clone> Clone for WireFlexibleResult<'_, T, E> {
185    fn clone(&self) -> Self {
186        Self {
187            raw: match self.raw.ordinal() {
188                ORD_OK => unsafe { self.raw.clone_inline_unchecked::<T>() },
189                ORD_ERR => unsafe { self.raw.clone_inline_unchecked::<E>() },
190                ORD_FRAMEWORK_ERR => unsafe {
191                    self.raw.clone_inline_unchecked::<WireFrameworkError>()
192                },
193                _ => unsafe { ::core::hint::unreachable_unchecked() },
194            },
195            _phantom: PhantomData,
196        }
197    }
198}
199
200impl<T, E> fmt::Debug for WireFlexibleResult<'_, T, E>
201where
202    T: fmt::Debug,
203    E: fmt::Debug,
204{
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        self.as_ref().fmt(f)
207    }
208}
209
210unsafe impl<D, T, E> Decode<D> for WireFlexibleResult<'static, T, E>
211where
212    D: Decoder + ?Sized,
213    T: Decode<D>,
214    E: Decode<D>,
215{
216    fn decode(slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
217        munge!(let Self { mut raw, _phantom: _ } = slot);
218
219        match RawWireUnion::encoded_ordinal(raw.as_mut()) {
220            ORD_OK => RawWireUnion::decode_as::<D, T>(raw, decoder)?,
221            ORD_ERR => RawWireUnion::decode_as::<D, E>(raw, decoder)?,
222            ORD_FRAMEWORK_ERR => RawWireUnion::decode_as::<D, WireFrameworkError>(raw, decoder)?,
223            ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
224        }
225
226        Ok(())
227    }
228}
229
230impl<T, E> Encodable for FlexibleResult<T, E>
231where
232    T: Encodable,
233    E: Encodable,
234{
235    type Encoded = WireFlexibleResult<'static, T::Encoded, E::Encoded>;
236}
237
238unsafe impl<Enc, T, E> Encode<Enc> for FlexibleResult<T, E>
239where
240    Enc: Encoder + ?Sized,
241    T: Encode<Enc>,
242    E: Encode<Enc>,
243{
244    fn encode(
245        self,
246        encoder: &mut Enc,
247        out: &mut MaybeUninit<Self::Encoded>,
248    ) -> Result<(), EncodeError> {
249        munge!(let WireFlexibleResult { raw, _phantom: _ } = out);
250
251        match self {
252            Self::Ok(value) => RawWireUnion::encode_as::<Enc, T>(value, ORD_OK, encoder, raw)?,
253            Self::Err(error) => RawWireUnion::encode_as::<Enc, E>(error, ORD_ERR, encoder, raw)?,
254            Self::FrameworkErr(error) => RawWireUnion::encode_as::<Enc, FrameworkError>(
255                error,
256                ORD_FRAMEWORK_ERR,
257                encoder,
258                raw,
259            )?,
260        }
261
262        Ok(())
263    }
264}
265
266unsafe impl<Enc, T, E> EncodeRef<Enc> for FlexibleResult<T, E>
267where
268    Enc: Encoder + ?Sized,
269    T: EncodeRef<Enc>,
270    E: EncodeRef<Enc>,
271{
272    fn encode_ref(
273        &self,
274        encoder: &mut Enc,
275        out: &mut MaybeUninit<Self::Encoded>,
276    ) -> Result<(), EncodeError> {
277        self.as_ref().encode(encoder, out)
278    }
279}
280
281impl<T, WT, E, WE> FromWire<WireFlexibleResult<'_, WT, WE>> for FlexibleResult<T, E>
282where
283    T: FromWire<WT>,
284    E: FromWire<WE>,
285{
286    fn from_wire(wire: WireFlexibleResult<'_, WT, WE>) -> Self {
287        match wire.to_flexible_result() {
288            FlexibleResult::Ok(value) => Self::Ok(T::from_wire(value)),
289            FlexibleResult::Err(error) => Self::Err(E::from_wire(error)),
290            FlexibleResult::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
291        }
292    }
293}
294
295impl<T, WT, E, WE> FromWireRef<WireFlexibleResult<'_, WT, WE>> for FlexibleResult<T, E>
296where
297    T: FromWireRef<WT>,
298    E: FromWireRef<WE>,
299{
300    fn from_wire_ref(wire: &WireFlexibleResult<'_, WT, WE>) -> Self {
301        match wire.as_ref() {
302            FlexibleResult::Ok(value) => Self::Ok(T::from_wire_ref(value)),
303            FlexibleResult::Err(error) => Self::Err(E::from_wire_ref(error)),
304            FlexibleResult::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
305        }
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use fidl_next_codec::{chunks, WireI32};
312
313    use super::{FlexibleResult, WireFlexibleResult};
314    use crate::testing::{assert_decoded, assert_encoded};
315    use crate::FrameworkError;
316
317    #[test]
318    fn encode_flexible_result() {
319        assert_encoded(
320            FlexibleResult::<(), i32>::Ok(()),
321            &chunks![
322                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323                0x01, 0x00,
324            ],
325        );
326        assert_encoded(
327            FlexibleResult::<(), i32>::Err(0x12345678),
328            &chunks![
329                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
330                0x01, 0x00,
331            ],
332        );
333        assert_encoded(
334            FlexibleResult::<(), i32>::FrameworkErr(FrameworkError::UnknownMethod),
335            &chunks![
336                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
337                0x01, 0x00,
338            ],
339        );
340    }
341
342    #[test]
343    fn decode_flexible_result() {
344        assert_decoded::<WireFlexibleResult<'_, (), WireI32>>(
345            &mut chunks![
346                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347                0x01, 0x00,
348            ],
349            |x| assert!(matches!(x.as_ref(), FlexibleResult::Ok(()))),
350        );
351        assert_decoded::<WireFlexibleResult<'_, (), WireI32>>(
352            &mut chunks![
353                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
354                0x01, 0x00,
355            ],
356            |x| assert!(matches!(x.as_ref(), FlexibleResult::Err(WireI32(0x12345678)))),
357        );
358        assert_decoded::<WireFlexibleResult<'_, (), WireI32>>(
359            &mut chunks![
360                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
361                0x01, 0x00,
362            ],
363            |x| {
364                assert!(matches!(
365                    x.as_ref(),
366                    FlexibleResult::FrameworkErr(FrameworkError::UnknownMethod)
367                ))
368            },
369        );
370    }
371}