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