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