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