fidl_next_protocol/
flexible.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,
12};
13
14use crate::{FrameworkError, WireFrameworkError};
15
16/// A flexible FIDL response.
17#[derive(Clone, Debug)]
18pub enum Flexible<T> {
19    /// The value of the flexible call when successful.
20    Ok(T),
21    /// The error indicating that the flexible call failed.
22    FrameworkErr(FrameworkError),
23}
24
25impl<T> Flexible<T> {
26    /// Converts from `&Flexible<T>` to `Flexible<&T>`.
27    pub fn as_ref(&self) -> Flexible<&T> {
28        match self {
29            Self::Ok(value) => Flexible::Ok(value),
30            Self::FrameworkErr(framework_error) => Flexible::FrameworkErr(*framework_error),
31        }
32    }
33}
34
35/// A flexible FIDL response.
36#[repr(transparent)]
37pub struct WireFlexible<'de, T> {
38    raw: RawWireUnion,
39    _phantom: PhantomData<(&'de mut [Chunk], T)>,
40}
41
42impl<T> Drop for WireFlexible<'_, T> {
43    fn drop(&mut self) {
44        match self.raw.ordinal() {
45            ORD_OK => {
46                let _ = unsafe { self.raw.get().read_unchecked::<T>() };
47            }
48            ORD_FRAMEWORK_ERR => {
49                let _ = unsafe { self.raw.get().read_unchecked::<WireFrameworkError>() };
50            }
51            _ => unsafe { ::core::hint::unreachable_unchecked() },
52        }
53    }
54}
55
56unsafe impl<T: Wire> Wire for WireFlexible<'static, T> {
57    type Decoded<'de> = WireFlexible<'de, T::Decoded<'de>>;
58
59    #[inline]
60    fn zero_padding(out: &mut MaybeUninit<Self>) {
61        munge!(let Self { raw, _phantom: _ } = out);
62        RawWireUnion::zero_padding(raw);
63    }
64}
65
66const ORD_OK: u64 = 1;
67const ORD_FRAMEWORK_ERR: u64 = 3;
68
69impl<T> WireFlexible<'_, T> {
70    /// Returns whether the flexible response is `Ok`.
71    pub fn is_ok(&self) -> bool {
72        self.raw.ordinal() == ORD_OK
73    }
74
75    /// Returns whether the flexible response is `FrameworkErr`.
76    pub fn is_framework_err(&self) -> bool {
77        self.raw.ordinal() == ORD_FRAMEWORK_ERR
78    }
79
80    /// Returns the `Ok` value of the response, if any.
81    pub fn ok(&self) -> Option<&T> {
82        self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
83    }
84
85    /// Returns the `FrameworkErr` value of the response, 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 response was not `Ok`.
94    pub fn unwrap(&self) -> &T {
95        self.ok().unwrap()
96    }
97
98    /// Returns the contained `FrameworkErr` value.
99    ///
100    /// Panics if the response was not `FrameworkErr`.
101    pub fn unwrap_framework_err(&self) -> FrameworkError {
102        self.framework_err().unwrap()
103    }
104
105    /// Returns a `Flexible` of a reference to the value or framework error.
106    pub fn as_ref(&self) -> Flexible<&T> {
107        match self.raw.ordinal() {
108            ORD_OK => unsafe { Flexible::Ok(self.raw.get().deref_unchecked()) },
109            ORD_FRAMEWORK_ERR => unsafe {
110                Flexible::FrameworkErr(
111                    (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into(),
112                )
113            },
114            _ => unsafe { ::core::hint::unreachable_unchecked() },
115        }
116    }
117
118    /// Returns a `Result` of the `Ok` value and a potential `FrameworkError`.
119    pub fn as_result(&self) -> Result<&T, FrameworkError> {
120        match self.raw.ordinal() {
121            ORD_OK => unsafe { Ok(self.raw.get().deref_unchecked()) },
122            ORD_FRAMEWORK_ERR => unsafe {
123                Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
124            },
125            _ => unsafe { ::core::hint::unreachable_unchecked() },
126        }
127    }
128
129    /// Returns a `Flexible` of an `Owned` value or framework error.
130    pub fn to_flexible(self) -> Flexible<T> {
131        let this = ManuallyDrop::new(self);
132        match this.raw.ordinal() {
133            ORD_OK => unsafe { Flexible::Ok(this.raw.get().read_unchecked()) },
134            ORD_FRAMEWORK_ERR => unsafe {
135                Flexible::FrameworkErr(this.raw.get().read_unchecked::<WireFrameworkError>().into())
136            },
137            _ => unsafe { ::core::hint::unreachable_unchecked() },
138        }
139    }
140}
141
142impl<T: Clone> Clone for WireFlexible<'_, T> {
143    fn clone(&self) -> Self {
144        Self {
145            raw: match self.raw.ordinal() {
146                ORD_OK => unsafe { self.raw.clone_inline_unchecked::<T>() },
147                ORD_FRAMEWORK_ERR => unsafe {
148                    self.raw.clone_inline_unchecked::<WireFrameworkError>()
149                },
150                _ => unsafe { ::core::hint::unreachable_unchecked() },
151            },
152            _phantom: PhantomData,
153        }
154    }
155}
156
157impl<T> fmt::Debug for WireFlexible<'_, T>
158where
159    T: fmt::Debug,
160{
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        self.as_ref().fmt(f)
163    }
164}
165
166unsafe impl<D, T> Decode<D> for WireFlexible<'static, T>
167where
168    D: Decoder + ?Sized,
169    T: Decode<D>,
170{
171    fn decode(slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
172        munge!(let Self { mut raw, _phantom: _ } = slot);
173
174        match RawWireUnion::encoded_ordinal(raw.as_mut()) {
175            ORD_OK => RawWireUnion::decode_as::<D, T>(raw, decoder)?,
176            ORD_FRAMEWORK_ERR => RawWireUnion::decode_as::<D, WireFrameworkError>(raw, decoder)?,
177            ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
178        }
179
180        Ok(())
181    }
182}
183
184impl<T> Encodable for Flexible<T>
185where
186    T: Encodable,
187{
188    type Encoded = WireFlexible<'static, T::Encoded>;
189}
190
191unsafe impl<E, T> Encode<E> for Flexible<T>
192where
193    E: Encoder + ?Sized,
194    T: Encode<E>,
195{
196    fn encode(
197        self,
198        encoder: &mut E,
199        out: &mut MaybeUninit<Self::Encoded>,
200    ) -> Result<(), EncodeError> {
201        munge!(let WireFlexible { raw, _phantom: _ } = out);
202
203        match self {
204            Self::Ok(value) => RawWireUnion::encode_as::<E, T>(value, ORD_OK, encoder, raw)?,
205            Self::FrameworkErr(error) => RawWireUnion::encode_as::<E, FrameworkError>(
206                error,
207                ORD_FRAMEWORK_ERR,
208                encoder,
209                raw,
210            )?,
211        }
212
213        Ok(())
214    }
215}
216
217unsafe impl<E, T> EncodeRef<E> for Flexible<T>
218where
219    E: Encoder + ?Sized,
220    T: EncodeRef<E>,
221{
222    fn encode_ref(
223        &self,
224        encoder: &mut E,
225        out: &mut MaybeUninit<Self::Encoded>,
226    ) -> Result<(), EncodeError> {
227        self.as_ref().encode(encoder, out)
228    }
229}
230
231impl<T, WT> FromWire<WireFlexible<'_, WT>> for Flexible<T>
232where
233    T: FromWire<WT>,
234{
235    fn from_wire(wire: WireFlexible<'_, WT>) -> Self {
236        match wire.to_flexible() {
237            Flexible::Ok(value) => Self::Ok(T::from_wire(value)),
238            Flexible::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
239        }
240    }
241}
242
243impl<T, WT> FromWireRef<WireFlexible<'_, WT>> for Flexible<T>
244where
245    T: FromWireRef<WT>,
246{
247    fn from_wire_ref(wire: &WireFlexible<'_, WT>) -> Self {
248        match wire.as_ref() {
249            Flexible::Ok(value) => Self::Ok(T::from_wire_ref(value)),
250            Flexible::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
251        }
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use fidl_next_codec::chunks;
258
259    use super::{Flexible, WireFlexible};
260    use crate::testing::{assert_decoded, assert_encoded};
261    use crate::FrameworkError;
262
263    #[test]
264    fn encode_flexible_result() {
265        assert_encoded(
266            Flexible::<()>::Ok(()),
267            &chunks![
268                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
269                0x01, 0x00,
270            ],
271        );
272        assert_encoded(
273            Flexible::<()>::FrameworkErr(FrameworkError::UnknownMethod),
274            &chunks![
275                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
276                0x01, 0x00,
277            ],
278        );
279    }
280
281    #[test]
282    fn decode_flexible_result() {
283        assert_decoded::<WireFlexible<'_, ()>>(
284            &mut chunks![
285                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
286                0x01, 0x00,
287            ],
288            |x| assert!(matches!(x.as_ref(), Flexible::Ok(()))),
289        );
290        assert_decoded::<WireFlexible<'_, ()>>(
291            &mut chunks![
292                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
293                0x01, 0x00,
294            ],
295            |x| {
296                assert!(matches!(x.as_ref(), Flexible::FrameworkErr(FrameworkError::UnknownMethod)))
297            },
298        );
299    }
300}