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