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;
7
8use fidl_next_codec::{
9    munge, Decode, DecodeError, Decoder, Encodable, Encode, EncodeError, Encoder, RawWireUnion,
10    Slot, TakeFrom, WireResult, ZeroPadding,
11};
12
13use crate::{FrameworkError, WireFrameworkError};
14
15/// A flexible FIDL result.
16#[derive(Debug)]
17pub enum FlexibleResult<T, E> {
18    /// The value of the flexible call when successful.
19    Ok(T),
20    /// The error returned from a successful flexible call.
21    Err(E),
22    /// The error indicating that the flexible call failed.
23    FrameworkErr(FrameworkError),
24}
25
26/// A flexible FIDL result.
27#[repr(transparent)]
28pub struct WireFlexibleResult<T, E> {
29    raw: RawWireUnion,
30    _phantom: PhantomData<(T, E)>,
31}
32
33unsafe impl<T, E> ZeroPadding for WireFlexibleResult<T, E> {
34    #[inline]
35    unsafe fn zero_padding(ptr: *mut Self) {
36        unsafe {
37            RawWireUnion::zero_padding(ptr.cast());
38        }
39    }
40}
41
42const ORD_OK: u64 = 1;
43const ORD_ERR: u64 = 2;
44const ORD_FRAMEWORK_ERR: u64 = 3;
45
46impl<T, E> WireFlexibleResult<T, E> {
47    /// Returns whether the flexible result is `Ok`.
48    pub fn is_ok(&self) -> bool {
49        self.raw.ordinal() == ORD_OK
50    }
51
52    /// Returns whether the flexible result if `Err`.
53    pub fn is_err(&self) -> bool {
54        self.raw.ordinal() == ORD_ERR
55    }
56
57    /// Returns whether the flexible result is `FrameworkErr`.
58    pub fn is_framework_err(&self) -> bool {
59        self.raw.ordinal() == ORD_FRAMEWORK_ERR
60    }
61
62    /// Returns the `Ok` value of the result, if any.
63    pub fn ok(&self) -> Option<&T> {
64        self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
65    }
66
67    /// Returns the `Err` value of the result, if any.
68    pub fn err(&self) -> Option<&E> {
69        self.is_err().then(|| unsafe { self.raw.get().deref_unchecked() })
70    }
71
72    /// Returns the `FrameworkErr` value of the result, if any.
73    pub fn framework_err(&self) -> Option<FrameworkError> {
74        self.is_framework_err()
75            .then(|| unsafe { (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into() })
76    }
77
78    /// Returns the contained `Ok` value.
79    ///
80    /// Panics if the result was not `Ok`.
81    pub fn unwrap(&self) -> &T {
82        self.ok().unwrap()
83    }
84
85    /// Returns the contained `Err` value.
86    ///
87    /// Panics if the result was not `Err`.
88    pub fn unwrap_err(&self) -> &E {
89        self.err().unwrap()
90    }
91
92    /// Returns the contained `FrameworkErr` value.
93    ///
94    /// Panics if the result was not `FrameworkErr`.
95    pub fn unwrap_framework_err(&self) -> FrameworkError {
96        self.framework_err().unwrap()
97    }
98
99    /// Returns a `Flexible` of a reference to the value or framework error.
100    pub fn as_ref(&self) -> FlexibleResult<&T, &E> {
101        match self.raw.ordinal() {
102            ORD_OK => unsafe { FlexibleResult::Ok(self.raw.get().deref_unchecked()) },
103            ORD_ERR => unsafe { FlexibleResult::Err(self.raw.get().deref_unchecked()) },
104            ORD_FRAMEWORK_ERR => unsafe {
105                FlexibleResult::FrameworkErr(
106                    (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into(),
107                )
108            },
109            _ => unsafe { ::core::hint::unreachable_unchecked() },
110        }
111    }
112
113    /// Returns a `Result` of the `Ok` value and a potential `FrameworkError`.
114    pub fn as_response(&self) -> Result<&WireResult<T, E>, FrameworkError> {
115        match self.raw.ordinal() {
116            ORD_OK | ORD_ERR => unsafe { Ok(&*(self as *const Self as *const WireResult<T, E>)) },
117            ORD_FRAMEWORK_ERR => unsafe {
118                Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
119            },
120            _ => unsafe { ::core::hint::unreachable_unchecked() },
121        }
122    }
123
124    /// Returns a nested `Result` of the `Ok` and `Err` values, and a potential `FrameworkError`.
125    pub fn as_result(&self) -> Result<Result<&T, &E>, FrameworkError> {
126        match self.raw.ordinal() {
127            ORD_OK => unsafe { Ok(Ok(self.raw.get().deref_unchecked())) },
128            ORD_ERR => unsafe { Ok(Err(self.raw.get().deref_unchecked())) },
129            ORD_FRAMEWORK_ERR => unsafe {
130                Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
131            },
132            _ => unsafe { ::core::hint::unreachable_unchecked() },
133        }
134    }
135}
136
137impl<T, E> fmt::Debug for WireFlexibleResult<T, E>
138where
139    T: fmt::Debug,
140    E: fmt::Debug,
141{
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        self.as_ref().fmt(f)
144    }
145}
146
147unsafe impl<D, T, E> Decode<D> for WireFlexibleResult<T, E>
148where
149    D: Decoder + ?Sized,
150    T: Decode<D>,
151    E: Decode<D>,
152{
153    fn decode(slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
154        munge!(let Self { mut raw, _phantom: _ } = slot);
155
156        match RawWireUnion::encoded_ordinal(raw.as_mut()) {
157            ORD_OK => RawWireUnion::decode_as::<D, T>(raw, decoder)?,
158            ORD_ERR => RawWireUnion::decode_as::<D, E>(raw, decoder)?,
159            ORD_FRAMEWORK_ERR => RawWireUnion::decode_as::<D, WireFrameworkError>(raw, decoder)?,
160            ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
161        }
162
163        Ok(())
164    }
165}
166
167impl<T, E> Encodable for FlexibleResult<T, E>
168where
169    T: Encodable,
170    E: Encodable,
171{
172    type Encoded = WireFlexibleResult<T::Encoded, E::Encoded>;
173}
174
175impl<Enc, T, E> Encode<Enc> for FlexibleResult<T, E>
176where
177    Enc: Encoder + ?Sized,
178    T: Encode<Enc>,
179    E: Encode<Enc>,
180{
181    fn encode(
182        &mut self,
183        encoder: &mut Enc,
184        slot: Slot<'_, Self::Encoded>,
185    ) -> Result<(), EncodeError> {
186        munge!(let WireFlexibleResult { raw, _phantom: _ } = slot);
187
188        match self {
189            Self::Ok(value) => RawWireUnion::encode_as::<Enc, T>(value, ORD_OK, encoder, raw)?,
190            Self::Err(error) => RawWireUnion::encode_as::<Enc, E>(error, ORD_ERR, encoder, raw)?,
191            Self::FrameworkErr(error) => RawWireUnion::encode_as::<Enc, FrameworkError>(
192                error,
193                ORD_FRAMEWORK_ERR,
194                encoder,
195                raw,
196            )?,
197        }
198
199        Ok(())
200    }
201}
202
203impl<T, WT, E, WE> TakeFrom<WireFlexibleResult<WT, WE>> for FlexibleResult<T, E>
204where
205    T: TakeFrom<WT>,
206    E: TakeFrom<WE>,
207{
208    fn take_from(from: &WireFlexibleResult<WT, WE>) -> Self {
209        match from.as_ref() {
210            FlexibleResult::Ok(value) => Self::Ok(T::take_from(value)),
211            FlexibleResult::Err(error) => Self::Err(E::take_from(error)),
212            FlexibleResult::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
213        }
214    }
215}