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