Skip to main content

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