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