Skip to main content

fidl_next_protocol/wire/
flexible.rs

1// Copyright 2026 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, forget};
8use core::ops::Deref;
9
10use fidl_next_codec::{
11    Chunk, Constrained, Decode, DecodeError, Decoder, Encode, EncodeError, Encoder, FromWire,
12    FromWireRef, IntoNatural, Slot, ValidationError, Wire, munge, wire,
13};
14
15const ORD_OK: u64 = 1;
16
17/// A flexible FIDL result union.
18#[repr(transparent)]
19pub struct Flexible<'de, T> {
20    raw: wire::Union,
21    _phantom: PhantomData<(&'de mut [Chunk], T)>,
22}
23
24impl<T> Drop for Flexible<'_, T> {
25    fn drop(&mut self) {
26        let _ = unsafe { self.raw.get().read_unchecked::<T>() };
27    }
28}
29
30impl<T> Constrained for Flexible<'_, T>
31where
32    T: Constrained<Constraint = ()>,
33{
34    type Constraint = ();
35
36    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
37        Ok(())
38    }
39}
40
41unsafe impl<T> Wire for Flexible<'static, T>
42where
43    T: Wire<Constraint = ()>,
44{
45    type Narrowed<'de> = Flexible<'de, T::Narrowed<'de>>;
46
47    #[inline]
48    fn zero_padding(out: &mut MaybeUninit<Self>) {
49        munge!(let Self { raw, _phantom: _ } = out);
50        wire::Union::zero_padding(raw);
51    }
52}
53
54impl<T> Deref for Flexible<'_, T> {
55    type Target = T;
56
57    fn deref(&self) -> &Self::Target {
58        unsafe { self.raw.get().deref_unchecked() }
59    }
60}
61
62impl<T> AsRef<T> for Flexible<'_, T> {
63    fn as_ref(&self) -> &T {
64        Deref::deref(self)
65    }
66}
67
68impl<T> Flexible<'_, T> {
69    /// Consumes the `Flexible`, returning the contained value.
70    pub fn into_inner(self) -> T {
71        let result = unsafe { self.raw.get().read_unchecked() };
72        forget(self);
73        result
74    }
75}
76
77impl<T: Clone> Clone for Flexible<'_, T> {
78    fn clone(&self) -> Self {
79        Self { raw: unsafe { self.raw.clone_inline_unchecked::<T>() }, _phantom: PhantomData }
80    }
81}
82
83impl<T: fmt::Debug> fmt::Debug for Flexible<'_, T> {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        self.as_ref().fmt(f)
86    }
87}
88
89unsafe impl<'de, D, T> Decode<D> for Flexible<'de, T>
90where
91    D: Decoder<'de> + ?Sized,
92    T: Decode<D, Constraint = ()>,
93{
94    fn decode(slot: Slot<'_, Self>, decoder: &mut D, _: ()) -> Result<(), DecodeError> {
95        munge!(let Self { mut raw, _phantom: _ } = slot);
96
97        let ordinal = wire::Union::encoded_ordinal(raw.as_mut());
98        if ordinal != ORD_OK {
99            return Err(DecodeError::InvalidUnionOrdinal(ordinal as usize));
100        }
101        wire::Union::decode_as::<D, T>(raw, decoder, ())?;
102        Ok(())
103    }
104}
105
106unsafe impl<E, W, T> Encode<Flexible<'static, W>, E> for crate::Flexible<T>
107where
108    E: Encoder + ?Sized,
109    W: Wire<Constraint = ()>,
110    T: Encode<W, E>,
111{
112    fn encode(
113        self,
114        encoder: &mut E,
115        out: &mut MaybeUninit<Flexible<'static, W>>,
116        _: (),
117    ) -> Result<(), EncodeError> {
118        munge!(let Flexible { raw, _phantom: _ } = out);
119        wire::Union::encode_as::<E, W>(self.0, ORD_OK, encoder, raw, ())?;
120        Ok(())
121    }
122}
123
124unsafe impl<'a, E, W, T> Encode<Flexible<'static, W>, E> for &'a crate::Flexible<T>
125where
126    E: Encoder + ?Sized,
127    W: Wire<Constraint = ()>,
128    &'a T: Encode<W, E>,
129{
130    fn encode(
131        self,
132        encoder: &mut E,
133        out: &mut MaybeUninit<Flexible<'static, W>>,
134        _: (),
135    ) -> Result<(), EncodeError> {
136        crate::Flexible(self.as_ref()).encode(encoder, out, ())
137    }
138}
139
140impl<T, W> FromWire<Flexible<'_, W>> for crate::Flexible<T>
141where
142    T: FromWire<W>,
143{
144    #[inline]
145    fn from_wire(wire: Flexible<'_, W>) -> Self {
146        crate::Flexible(T::from_wire(wire.into_inner()))
147    }
148}
149
150impl<T: IntoNatural> IntoNatural for Flexible<'_, T> {
151    type Natural = crate::Flexible<T::Natural>;
152}
153
154impl<T, W> FromWireRef<Flexible<'_, W>> for crate::Flexible<T>
155where
156    T: FromWireRef<W>,
157{
158    #[inline]
159    fn from_wire_ref(wire: &Flexible<'_, W>) -> Self {
160        crate::Flexible(T::from_wire_ref(wire.as_ref()))
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::Flexible;
167
168    use fidl_next_codec::{DecoderExt as _, EncoderExt as _, chunks, wire};
169
170    #[test]
171    fn encode_flexible() {
172        assert_eq!(
173            Vec::encode(crate::Flexible::<i32>(0x12345678)).unwrap(),
174            chunks![
175                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
176                0x01, 0x00,
177            ],
178        );
179    }
180
181    #[test]
182    fn decode_result() {
183        assert_eq!(
184            chunks![
185                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
186                0x01, 0x00,
187            ]
188            .as_mut_slice()
189            .decode::<Flexible<'_, wire::Int32>>()
190            .unwrap()
191            .as_ref()
192            .0,
193            0x12345678,
194        );
195    }
196}