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