1use 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, 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 is_ok(&self) -> bool {
28 matches!(self, Self::Ok(_))
29 }
30
31 pub fn is_framework_err(&self) -> bool {
33 matches!(self, Self::FrameworkErr(_))
34 }
35
36 pub fn ok(self) -> Option<T> {
38 if let Self::Ok(value) = self { Some(value) } else { None }
39 }
40
41 pub fn framework_err(self) -> Option<FrameworkError> {
43 if let Self::FrameworkErr(error) = self { Some(error) } else { None }
44 }
45
46 pub fn unwrap(self) -> T {
50 self.ok().unwrap()
51 }
52
53 pub fn unwrap_framework_err(self) -> FrameworkError {
57 self.framework_err().unwrap()
58 }
59
60 pub fn as_ref(&self) -> Flexible<&T> {
62 match self {
63 Self::Ok(value) => Flexible::Ok(value),
64 Self::FrameworkErr(framework_error) => Flexible::FrameworkErr(*framework_error),
65 }
66 }
67}
68
69#[repr(transparent)]
71pub struct WireFlexible<'de, T> {
72 raw: RawWireUnion,
73 _phantom: PhantomData<(&'de mut [Chunk], T)>,
74}
75
76impl<T> Drop for WireFlexible<'_, T> {
77 fn drop(&mut self) {
78 match self.raw.ordinal() {
79 ORD_OK => {
80 let _ = unsafe { self.raw.get().read_unchecked::<T>() };
81 }
82 ORD_FRAMEWORK_ERR => {
83 let _ = unsafe { self.raw.get().read_unchecked::<WireFrameworkError>() };
84 }
85 _ => unsafe { ::core::hint::unreachable_unchecked() },
86 }
87 }
88}
89
90unsafe impl<T: Wire> Wire for WireFlexible<'static, T> {
91 type Owned<'de> = WireFlexible<'de, T::Owned<'de>>;
92
93 #[inline]
94 fn zero_padding(out: &mut MaybeUninit<Self>) {
95 munge!(let Self { raw, _phantom: _ } = out);
96 RawWireUnion::zero_padding(raw);
97 }
98}
99
100impl<T: Constrained<Constraint = ()>> Unconstrained for WireFlexible<'_, T> {}
101
102const ORD_OK: u64 = 1;
103const ORD_FRAMEWORK_ERR: u64 = 3;
104
105impl<T> WireFlexible<'_, T> {
106 pub fn is_ok(&self) -> bool {
108 self.raw.ordinal() == ORD_OK
109 }
110
111 pub fn is_framework_err(&self) -> bool {
113 self.raw.ordinal() == ORD_FRAMEWORK_ERR
114 }
115
116 pub fn ok(&self) -> Option<&T> {
118 self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
119 }
120
121 pub fn framework_err(&self) -> Option<FrameworkError> {
123 self.is_framework_err()
124 .then(|| unsafe { (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into() })
125 }
126
127 pub fn unwrap(&self) -> &T {
131 self.ok().unwrap()
132 }
133
134 pub fn unwrap_framework_err(&self) -> FrameworkError {
138 self.framework_err().unwrap()
139 }
140
141 pub fn as_ref(&self) -> Flexible<&T> {
143 match self.raw.ordinal() {
144 ORD_OK => unsafe { Flexible::Ok(self.raw.get().deref_unchecked()) },
145 ORD_FRAMEWORK_ERR => unsafe {
146 Flexible::FrameworkErr(
147 (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into(),
148 )
149 },
150 _ => unsafe { ::core::hint::unreachable_unchecked() },
151 }
152 }
153
154 pub fn as_result(&self) -> Result<&T, FrameworkError> {
156 match self.raw.ordinal() {
157 ORD_OK => unsafe { Ok(self.raw.get().deref_unchecked()) },
158 ORD_FRAMEWORK_ERR => unsafe {
159 Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
160 },
161 _ => unsafe { ::core::hint::unreachable_unchecked() },
162 }
163 }
164
165 pub fn to_flexible(self) -> Flexible<T> {
167 let this = ManuallyDrop::new(self);
168 match this.raw.ordinal() {
169 ORD_OK => unsafe { Flexible::Ok(this.raw.get().read_unchecked()) },
170 ORD_FRAMEWORK_ERR => unsafe {
171 Flexible::FrameworkErr(this.raw.get().read_unchecked::<WireFrameworkError>().into())
172 },
173 _ => unsafe { ::core::hint::unreachable_unchecked() },
174 }
175 }
176}
177
178impl<T: Clone> Clone for WireFlexible<'_, T> {
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_FRAMEWORK_ERR => unsafe {
184 self.raw.clone_inline_unchecked::<WireFrameworkError>()
185 },
186 _ => unsafe { ::core::hint::unreachable_unchecked() },
187 },
188 _phantom: PhantomData,
189 }
190 }
191}
192
193impl<T> fmt::Debug for WireFlexible<'_, T>
194where
195 T: fmt::Debug,
196{
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 self.as_ref().fmt(f)
199 }
200}
201
202unsafe impl<D, T> Decode<D> for WireFlexible<'static, T>
203where
204 D: Decoder + ?Sized,
205 T: Decode<D> + Constrained<Constraint = ()>,
206{
207 fn decode(
208 slot: Slot<'_, Self>,
209 decoder: &mut D,
210 constraint: <Self as Constrained>::Constraint,
211 ) -> Result<(), DecodeError> {
212 munge!(let Self { mut raw, _phantom: _ } = slot);
213
214 match RawWireUnion::encoded_ordinal(raw.as_mut()) {
215 ORD_OK => RawWireUnion::decode_as::<D, T>(raw, decoder, constraint)?,
216 ORD_FRAMEWORK_ERR => {
217 RawWireUnion::decode_as::<D, WireFrameworkError>(raw, decoder, ())?
218 }
219 ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
220 }
221
222 Ok(())
223 }
224}
225
226unsafe impl<E, WT, T> Encode<WireFlexible<'static, WT>, E> for Flexible<T>
227where
228 E: Encoder + ?Sized,
229 WT: Constrained<Constraint = ()> + Wire,
230 T: Encode<WT, E>,
231{
232 fn encode(
233 self,
234 encoder: &mut E,
235 out: &mut MaybeUninit<WireFlexible<'static, WT>>,
236 constraint: WT::Constraint,
237 ) -> Result<(), EncodeError> {
238 munge!(let WireFlexible { raw, _phantom: _ } = out);
239
240 match self {
241 Self::Ok(value) => {
242 RawWireUnion::encode_as::<E, WT>(value, ORD_OK, encoder, raw, constraint)?
243 }
244 Self::FrameworkErr(error) => RawWireUnion::encode_as::<E, WireFrameworkError>(
245 error,
246 ORD_FRAMEWORK_ERR,
247 encoder,
248 raw,
249 (),
250 )?,
251 }
252
253 Ok(())
254 }
255}
256
257unsafe impl<'a, E, WT, T> Encode<WireFlexible<'static, WT>, E> for &'a Flexible<T>
258where
259 E: Encoder + ?Sized,
260 WT: Constrained<Constraint = ()> + Wire,
261 &'a T: Encode<WT, E>,
262{
263 fn encode(
264 self,
265 encoder: &mut E,
266 out: &mut MaybeUninit<WireFlexible<'static, WT>>,
267 constraint: WT::Constraint,
268 ) -> Result<(), EncodeError> {
269 self.as_ref().encode(encoder, out, constraint)
270 }
271}
272
273impl<T, WT> FromWire<WireFlexible<'_, WT>> for Flexible<T>
274where
275 T: FromWire<WT>,
276{
277 fn from_wire(wire: WireFlexible<'_, WT>) -> Self {
278 match wire.to_flexible() {
279 Flexible::Ok(value) => Self::Ok(T::from_wire(value)),
280 Flexible::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
281 }
282 }
283}
284
285impl<T: IntoNatural> IntoNatural for WireFlexible<'_, T> {
286 type Natural = Flexible<T::Natural>;
287}
288
289impl<T, WT> FromWireRef<WireFlexible<'_, WT>> for Flexible<T>
290where
291 T: FromWireRef<WT>,
292{
293 fn from_wire_ref(wire: &WireFlexible<'_, WT>) -> Self {
294 match wire.as_ref() {
295 Flexible::Ok(value) => Self::Ok(T::from_wire_ref(value)),
296 Flexible::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
297 }
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use fidl_next_codec::chunks;
304
305 use super::{Flexible, WireFlexible};
306 use crate::FrameworkError;
307 use crate::testing::{assert_decoded, assert_encoded};
308
309 #[test]
310 fn encode_flexible_result() {
311 assert_encoded(
312 Flexible::<()>::Ok(()),
313 &chunks![
314 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
315 0x01, 0x00,
316 ],
317 );
318 assert_encoded(
319 Flexible::<()>::FrameworkErr(FrameworkError::UnknownMethod),
320 &chunks![
321 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
322 0x01, 0x00,
323 ],
324 );
325 }
326
327 #[test]
328 fn decode_flexible_result() {
329 assert_decoded::<WireFlexible<'_, ()>>(
330 &mut chunks![
331 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332 0x01, 0x00,
333 ],
334 |x| assert!(matches!(x.as_ref(), Flexible::Ok(()))),
335 );
336 assert_decoded::<WireFlexible<'_, ()>>(
337 &mut chunks![
338 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
339 0x01, 0x00,
340 ],
341 |x| {
342 assert!(matches!(x.as_ref(), Flexible::FrameworkErr(FrameworkError::UnknownMethod)))
343 },
344 );
345 }
346}