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;
7
8use fidl_next_codec::{
9    munge, Decode, DecodeError, Decoder, Encodable, Encode, EncodeError, Encoder, RawWireUnion,
10    Slot, TakeFrom, ZeroPadding,
11};
12
13use crate::{FrameworkError, WireFrameworkError};
14
15/// A flexible FIDL response.
16#[derive(Debug)]
17pub enum Flexible<T> {
18    /// The value of the flexible call when successful.
19    Ok(T),
20    /// The error indicating that the flexible call failed.
21    FrameworkErr(FrameworkError),
22}
23
24/// A flexible FIDL response.
25#[repr(transparent)]
26pub struct WireFlexible<T> {
27    raw: RawWireUnion,
28    _phantom: PhantomData<T>,
29}
30
31unsafe impl<T> ZeroPadding for WireFlexible<T> {
32    #[inline]
33    unsafe fn zero_padding(ptr: *mut Self) {
34        unsafe {
35            RawWireUnion::zero_padding(ptr.cast());
36        }
37    }
38}
39
40const ORD_OK: u64 = 1;
41const ORD_FRAMEWORK_ERR: u64 = 3;
42
43impl<T> WireFlexible<T> {
44    /// Returns whether the flexible response is `Ok`.
45    pub fn is_ok(&self) -> bool {
46        self.raw.ordinal() == ORD_OK
47    }
48
49    /// Returns whether the flexible response is `FrameworkErr`.
50    pub fn is_framework_err(&self) -> bool {
51        self.raw.ordinal() == ORD_FRAMEWORK_ERR
52    }
53
54    /// Returns the `Ok` value of the response, if any.
55    pub fn ok(&self) -> Option<&T> {
56        self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
57    }
58
59    /// Returns the `FrameworkErr` value of the response, if any.
60    pub fn framework_err(&self) -> Option<FrameworkError> {
61        self.is_framework_err()
62            .then(|| unsafe { (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into() })
63    }
64
65    /// Returns the contained `Ok` value.
66    ///
67    /// Panics if the response was not `Ok`.
68    pub fn unwrap(&self) -> &T {
69        self.ok().unwrap()
70    }
71
72    /// Returns the contained `FrameworkErr` value.
73    ///
74    /// Panics if the response was not `FrameworkErr`.
75    pub fn unwrap_framework_err(&self) -> FrameworkError {
76        self.framework_err().unwrap()
77    }
78
79    /// Returns a `Flexible` of a reference to the value or framework error.
80    pub fn as_ref(&self) -> Flexible<&T> {
81        match self.raw.ordinal() {
82            ORD_OK => unsafe { Flexible::Ok(self.raw.get().deref_unchecked()) },
83            ORD_FRAMEWORK_ERR => unsafe {
84                Flexible::FrameworkErr(
85                    (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into(),
86                )
87            },
88            _ => unsafe { ::core::hint::unreachable_unchecked() },
89        }
90    }
91
92    /// Returns a `Result` of the `Ok` value and a potential `FrameworkError`.`
93    pub fn as_result(&self) -> Result<&T, FrameworkError> {
94        match self.raw.ordinal() {
95            ORD_OK => unsafe { Ok(self.raw.get().deref_unchecked()) },
96            ORD_FRAMEWORK_ERR => unsafe {
97                Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
98            },
99            _ => unsafe { ::core::hint::unreachable_unchecked() },
100        }
101    }
102}
103
104impl<T> fmt::Debug for WireFlexible<T>
105where
106    T: fmt::Debug,
107{
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        self.as_ref().fmt(f)
110    }
111}
112
113unsafe impl<D, T> Decode<D> for WireFlexible<T>
114where
115    D: Decoder + ?Sized,
116    T: Decode<D>,
117{
118    fn decode(slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
119        munge!(let Self { mut raw, _phantom: _ } = slot);
120
121        match RawWireUnion::encoded_ordinal(raw.as_mut()) {
122            ORD_OK => RawWireUnion::decode_as::<D, T>(raw, decoder)?,
123            ORD_FRAMEWORK_ERR => RawWireUnion::decode_as::<D, WireFrameworkError>(raw, decoder)?,
124            ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
125        }
126
127        Ok(())
128    }
129}
130
131impl<T> Encodable for Flexible<T>
132where
133    T: Encodable,
134{
135    type Encoded = WireFlexible<T::Encoded>;
136}
137
138impl<E, T> Encode<E> for Flexible<T>
139where
140    E: Encoder + ?Sized,
141    T: Encode<E>,
142{
143    fn encode(
144        &mut self,
145        encoder: &mut E,
146        slot: Slot<'_, Self::Encoded>,
147    ) -> Result<(), EncodeError> {
148        munge!(let WireFlexible { raw, _phantom: _ } = slot);
149
150        match self {
151            Self::Ok(value) => RawWireUnion::encode_as::<E, T>(value, ORD_OK, encoder, raw)?,
152            Self::FrameworkErr(error) => RawWireUnion::encode_as::<E, FrameworkError>(
153                error,
154                ORD_FRAMEWORK_ERR,
155                encoder,
156                raw,
157            )?,
158        }
159
160        Ok(())
161    }
162}
163
164impl<T, WT> TakeFrom<WireFlexible<WT>> for Flexible<T>
165where
166    T: TakeFrom<WT>,
167{
168    fn take_from(from: &WireFlexible<WT>) -> Self {
169        match from.as_ref() {
170            Flexible::Ok(value) => Self::Ok(T::take_from(value)),
171            Flexible::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
172        }
173    }
174}