Skip to main content

fidl_next_protocol/wire/
flexible_result.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 result.
17#[repr(transparent)]
18pub struct FlexibleResult<'de, T, E> {
19    raw: wire::Union,
20    _phantom: PhantomData<(&'de mut [Chunk], T, E)>,
21}
22
23impl<T, E> Drop for FlexibleResult<'_, T, E> {
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_ERR => {
30                let _ = unsafe { self.raw.get().read_unchecked::<E>() };
31            }
32            ORD_FRAMEWORK_ERR => {
33                let _ = unsafe { self.raw.get().read_unchecked::<wire::FrameworkError>() };
34            }
35            _ => unsafe { ::core::hint::unreachable_unchecked() },
36        }
37    }
38}
39
40impl<T, E> Constrained for FlexibleResult<'_, T, E>
41where
42    T: Constrained<Constraint = ()>,
43    E: Constrained<Constraint = ()>,
44{
45    type Constraint = ();
46
47    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
48        Ok(())
49    }
50}
51
52unsafe impl<T, E> Wire for FlexibleResult<'static, T, E>
53where
54    T: Wire<Constraint = ()>,
55    E: Wire<Constraint = ()>,
56{
57    type Narrowed<'de> = FlexibleResult<'de, T::Narrowed<'de>, E::Narrowed<'de>>;
58
59    #[inline]
60    fn zero_padding(out: &mut MaybeUninit<Self>) {
61        munge!(let Self { raw, _phantom: _ } = out);
62        wire::Union::zero_padding(raw);
63    }
64}
65
66const ORD_OK: u64 = 1;
67const ORD_ERR: u64 = 2;
68const ORD_FRAMEWORK_ERR: u64 = 3;
69
70impl<'de, T, E> FlexibleResult<'de, T, E> {
71    /// Returns whether the flexible result is `Ok`.
72    pub fn is_ok(&self) -> bool {
73        self.raw.ordinal() == ORD_OK
74    }
75
76    /// Returns whether the flexible result if `Err`.
77    pub fn is_err(&self) -> bool {
78        self.raw.ordinal() == ORD_ERR
79    }
80
81    /// Returns whether the flexible result is `FrameworkErr`.
82    pub fn is_framework_err(&self) -> bool {
83        self.raw.ordinal() == ORD_FRAMEWORK_ERR
84    }
85
86    /// Returns the `Ok` value of the result, if any.
87    pub fn ok(&self) -> Option<&T> {
88        self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
89    }
90
91    /// Returns the `Err` value of the result, if any.
92    pub fn err(&self) -> Option<&E> {
93        self.is_err().then(|| unsafe { self.raw.get().deref_unchecked() })
94    }
95
96    /// Returns the `FrameworkErr` value of the result, if any.
97    pub fn framework_err(&self) -> Option<crate::FrameworkError> {
98        self.is_framework_err()
99            .then(|| unsafe { (*self.raw.get().deref_unchecked::<wire::FrameworkError>()).into() })
100    }
101
102    /// Returns the contained `Ok` value.
103    ///
104    /// Panics if the result was not `Ok`.
105    pub fn unwrap(&self) -> &T {
106        self.ok().unwrap()
107    }
108
109    /// Returns the contained `Err` value.
110    ///
111    /// Panics if the result was not `Err`.
112    pub fn unwrap_err(&self) -> &E {
113        self.err().unwrap()
114    }
115
116    /// Returns the contained `FrameworkErr` value.
117    ///
118    /// Panics if the result was not `FrameworkErr`.
119    pub fn unwrap_framework_err(&self) -> crate::FrameworkError {
120        self.framework_err().unwrap()
121    }
122
123    /// Returns a `FlexibleResult` of a reference to the value or framework error.
124    pub fn as_ref(&self) -> crate::FlexibleResult<&T, &E> {
125        match self.raw.ordinal() {
126            ORD_OK => unsafe { crate::FlexibleResult::Ok(self.raw.get().deref_unchecked()) },
127            ORD_ERR => unsafe { crate::FlexibleResult::Err(self.raw.get().deref_unchecked()) },
128            ORD_FRAMEWORK_ERR => unsafe {
129                crate::FlexibleResult::FrameworkErr(
130                    (*self.raw.get().deref_unchecked::<wire::FrameworkError>()).into(),
131                )
132            },
133            _ => unsafe { ::core::hint::unreachable_unchecked() },
134        }
135    }
136
137    /// Returns a `Result` of the `Ok` value and a potential `FrameworkError`.
138    pub fn as_response(&self) -> Result<&wire::Result<'_, T, E>, crate::FrameworkError> {
139        match self.raw.ordinal() {
140            ORD_OK | ORD_ERR => unsafe {
141                Ok(&*(self as *const Self as *const wire::Result<'_, T, E>))
142            },
143            ORD_FRAMEWORK_ERR => unsafe {
144                Err((*self.raw.get().deref_unchecked::<wire::FrameworkError>()).into())
145            },
146            _ => unsafe { ::core::hint::unreachable_unchecked() },
147        }
148    }
149
150    /// Returns a nested `Result` of the `Ok` and `Err` values, and a potential `FrameworkError`.
151    pub fn as_result(&self) -> Result<Result<&T, &E>, crate::FrameworkError> {
152        match self.raw.ordinal() {
153            ORD_OK => unsafe { Ok(Ok(self.raw.get().deref_unchecked())) },
154            ORD_ERR => unsafe { Ok(Err(self.raw.get().deref_unchecked())) },
155            ORD_FRAMEWORK_ERR => unsafe {
156                Err((*self.raw.get().deref_unchecked::<wire::FrameworkError>()).into())
157            },
158            _ => unsafe { ::core::hint::unreachable_unchecked() },
159        }
160    }
161
162    /// Returns a `FlexibleResult` of a value or framework error.
163    pub fn to_flexible_result(self) -> crate::FlexibleResult<T, E> {
164        let this = ManuallyDrop::new(self);
165        match this.raw.ordinal() {
166            ORD_OK => unsafe { crate::FlexibleResult::Ok(this.raw.get().read_unchecked()) },
167            ORD_ERR => unsafe { crate::FlexibleResult::Err(this.raw.get().read_unchecked()) },
168            ORD_FRAMEWORK_ERR => unsafe {
169                crate::FlexibleResult::FrameworkErr(
170                    this.raw.get().read_unchecked::<wire::FrameworkError>().into(),
171                )
172            },
173            _ => unsafe { ::core::hint::unreachable_unchecked() },
174        }
175    }
176}
177
178impl<T: Clone, E: Clone> Clone for FlexibleResult<'_, T, E> {
179    fn clone(&self) -> Self {
180        Self {
181            raw: match self.raw.ordinal() {
182                ORD_OK => unsafe { self.raw.clone_inline_unchecked::<T>() },
183                ORD_ERR => unsafe { self.raw.clone_inline_unchecked::<E>() },
184                ORD_FRAMEWORK_ERR => unsafe {
185                    self.raw.clone_inline_unchecked::<wire::FrameworkError>()
186                },
187                _ => unsafe { ::core::hint::unreachable_unchecked() },
188            },
189            _phantom: PhantomData,
190        }
191    }
192}
193
194impl<T, E> fmt::Debug for FlexibleResult<'_, T, E>
195where
196    T: fmt::Debug,
197    E: fmt::Debug,
198{
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        self.as_ref().fmt(f)
201    }
202}
203
204unsafe impl<'de, D, T, E> Decode<D> for FlexibleResult<'de, T, E>
205where
206    D: Decoder<'de> + ?Sized,
207    T: Decode<D, Constraint = ()>,
208    E: Decode<D, Constraint = ()>,
209{
210    fn decode(slot: Slot<'_, Self>, decoder: &mut D, _: ()) -> Result<(), DecodeError> {
211        munge!(let Self { mut raw, _phantom: _ } = slot);
212
213        match wire::Union::encoded_ordinal(raw.as_mut()) {
214            ORD_OK => wire::Union::decode_as::<D, T>(raw, decoder, ())?,
215            ORD_ERR => wire::Union::decode_as::<D, E>(raw, decoder, ())?,
216            ORD_FRAMEWORK_ERR => {
217                wire::Union::decode_as::<D, wire::FrameworkError>(raw, decoder, ())?
218            }
219            ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
220        }
221
222        Ok(())
223    }
224}
225
226unsafe impl<Enc, WT, T, WE, E> Encode<FlexibleResult<'static, WT, WE>, Enc>
227    for crate::FlexibleResult<T, E>
228where
229    Enc: Encoder + ?Sized,
230    WT: Wire<Constraint = ()>,
231    T: Encode<WT, Enc>,
232    WE: Wire<Constraint = ()>,
233    E: Encode<WE, Enc>,
234{
235    fn encode(
236        self,
237        encoder: &mut Enc,
238        out: &mut MaybeUninit<FlexibleResult<'static, WT, WE>>,
239        _: (),
240    ) -> Result<(), EncodeError> {
241        munge!(let FlexibleResult { raw, _phantom: _ } = out);
242
243        match self {
244            Self::Ok(value) => wire::Union::encode_as::<Enc, WT>(value, ORD_OK, encoder, raw, ())?,
245            Self::Err(error) => {
246                wire::Union::encode_as::<Enc, WE>(error, ORD_ERR, encoder, raw, ())?
247            }
248            Self::FrameworkErr(error) => wire::Union::encode_as::<Enc, wire::FrameworkError>(
249                error,
250                ORD_FRAMEWORK_ERR,
251                encoder,
252                raw,
253                (),
254            )?,
255        }
256
257        Ok(())
258    }
259}
260
261unsafe impl<'a, Enc, WT, T, WE, E> Encode<FlexibleResult<'static, WT, WE>, Enc>
262    for &'a crate::FlexibleResult<T, E>
263where
264    Enc: Encoder + ?Sized,
265    WT: Wire<Constraint = ()>,
266    &'a T: Encode<WT, Enc>,
267    WE: Wire<Constraint = ()>,
268    &'a E: Encode<WE, Enc>,
269{
270    fn encode(
271        self,
272        encoder: &mut Enc,
273        out: &mut MaybeUninit<FlexibleResult<'static, WT, WE>>,
274        _: (),
275    ) -> Result<(), EncodeError> {
276        self.as_ref().encode(encoder, out, ())
277    }
278}
279
280impl<T, WT, E, WE> FromWire<FlexibleResult<'_, WT, WE>> for crate::FlexibleResult<T, E>
281where
282    T: FromWire<WT>,
283    E: FromWire<WE>,
284{
285    fn from_wire(wire: FlexibleResult<'_, WT, WE>) -> Self {
286        match wire.to_flexible_result() {
287            crate::FlexibleResult::Ok(value) => Self::Ok(T::from_wire(value)),
288            crate::FlexibleResult::Err(error) => Self::Err(E::from_wire(error)),
289            crate::FlexibleResult::FrameworkErr(framework_error) => {
290                Self::FrameworkErr(framework_error)
291            }
292        }
293    }
294}
295
296impl<T: IntoNatural, E: IntoNatural> IntoNatural for FlexibleResult<'_, T, E> {
297    type Natural = crate::FlexibleResult<T::Natural, E::Natural>;
298}
299
300impl<T, WT, E, WE> FromWireRef<FlexibleResult<'_, WT, WE>> for crate::FlexibleResult<T, E>
301where
302    T: FromWireRef<WT>,
303    E: FromWireRef<WE>,
304{
305    fn from_wire_ref(wire: &FlexibleResult<'_, WT, WE>) -> Self {
306        match wire.as_ref() {
307            crate::FlexibleResult::Ok(value) => Self::Ok(T::from_wire_ref(value)),
308            crate::FlexibleResult::Err(error) => Self::Err(E::from_wire_ref(error)),
309            crate::FlexibleResult::FrameworkErr(framework_error) => {
310                Self::FrameworkErr(framework_error)
311            }
312        }
313    }
314}
315
316#[cfg(test)]
317mod tests {
318    use fidl_next_codec::{DecoderExt as _, EncoderExt as _, chunks};
319
320    use crate::wire;
321
322    #[test]
323    fn encode_flexible_result() {
324        assert_eq!(
325            Vec::encode(crate::FlexibleResult::<(), i32>::Ok(())).unwrap(),
326            chunks![
327                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328                0x01, 0x00,
329            ],
330        );
331        assert_eq!(
332            Vec::encode(crate::FlexibleResult::<(), i32>::Err(0x12345678)).unwrap(),
333            chunks![
334                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
335                0x01, 0x00,
336            ],
337        );
338        assert_eq!(
339            Vec::encode(crate::FlexibleResult::<(), i32>::FrameworkErr(
340                crate::FrameworkError::UnknownMethod
341            ))
342            .unwrap(),
343            chunks![
344                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
345                0x01, 0x00,
346            ],
347        );
348    }
349
350    #[test]
351    fn decode_flexible_result() {
352        assert_eq!(
353            chunks![
354                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
355                0x01, 0x00,
356            ]
357            .as_mut_slice()
358            .decode::<wire::FlexibleResult<'_, (), wire::Int32>>()
359            .unwrap()
360            .as_ref(),
361            crate::FlexibleResult::<_, &wire::Int32>::Ok(&()),
362        );
363        assert_eq!(
364            chunks![
365                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
366                0x01, 0x00,
367            ]
368            .as_mut_slice()
369            .decode::<wire::FlexibleResult<'_, (), wire::Int32>>()
370            .unwrap()
371            .as_ref(),
372            crate::FlexibleResult::<&(), _>::Err(&wire::Int32(0x12345678)),
373        );
374        assert_eq!(
375            chunks![
376                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
377                0x01, 0x00,
378            ]
379            .as_mut_slice()
380            .decode::<wire::FlexibleResult<'_, (), wire::Int32>>()
381            .unwrap()
382            .as_ref(),
383            crate::FlexibleResult::<&(), &wire::Int32>::FrameworkErr(
384                crate::FrameworkError::UnknownMethod
385            ),
386        );
387    }
388}