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