1use core::fmt;
6use core::marker::PhantomData;
7use core::mem::{ManuallyDrop, MaybeUninit};
8
9use fidl_next_codec::{
10 munge, Chunk, Decode, DecodeError, Decoder, Encodable, Encode, EncodeError, EncodeRef, Encoder,
11 FromWire, FromWireRef, RawWireUnion, Slot, Wire,
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
66const ORD_OK: u64 = 1;
67const ORD_FRAMEWORK_ERR: u64 = 3;
68
69impl<T> WireFlexible<'_, T> {
70 pub fn is_ok(&self) -> bool {
72 self.raw.ordinal() == ORD_OK
73 }
74
75 pub fn is_framework_err(&self) -> bool {
77 self.raw.ordinal() == ORD_FRAMEWORK_ERR
78 }
79
80 pub fn ok(&self) -> Option<&T> {
82 self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
83 }
84
85 pub fn framework_err(&self) -> Option<FrameworkError> {
87 self.is_framework_err()
88 .then(|| unsafe { (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into() })
89 }
90
91 pub fn unwrap(&self) -> &T {
95 self.ok().unwrap()
96 }
97
98 pub fn unwrap_framework_err(&self) -> FrameworkError {
102 self.framework_err().unwrap()
103 }
104
105 pub fn as_ref(&self) -> Flexible<&T> {
107 match self.raw.ordinal() {
108 ORD_OK => unsafe { Flexible::Ok(self.raw.get().deref_unchecked()) },
109 ORD_FRAMEWORK_ERR => unsafe {
110 Flexible::FrameworkErr(
111 (*self.raw.get().deref_unchecked::<WireFrameworkError>()).into(),
112 )
113 },
114 _ => unsafe { ::core::hint::unreachable_unchecked() },
115 }
116 }
117
118 pub fn as_result(&self) -> Result<&T, FrameworkError> {
120 match self.raw.ordinal() {
121 ORD_OK => unsafe { Ok(self.raw.get().deref_unchecked()) },
122 ORD_FRAMEWORK_ERR => unsafe {
123 Err((*self.raw.get().deref_unchecked::<WireFrameworkError>()).into())
124 },
125 _ => unsafe { ::core::hint::unreachable_unchecked() },
126 }
127 }
128
129 pub fn to_flexible(self) -> Flexible<T> {
131 let this = ManuallyDrop::new(self);
132 match this.raw.ordinal() {
133 ORD_OK => unsafe { Flexible::Ok(this.raw.get().read_unchecked()) },
134 ORD_FRAMEWORK_ERR => unsafe {
135 Flexible::FrameworkErr(this.raw.get().read_unchecked::<WireFrameworkError>().into())
136 },
137 _ => unsafe { ::core::hint::unreachable_unchecked() },
138 }
139 }
140}
141
142impl<T: Clone> Clone for WireFlexible<'_, T> {
143 fn clone(&self) -> Self {
144 Self {
145 raw: match self.raw.ordinal() {
146 ORD_OK => unsafe { self.raw.clone_inline_unchecked::<T>() },
147 ORD_FRAMEWORK_ERR => unsafe {
148 self.raw.clone_inline_unchecked::<WireFrameworkError>()
149 },
150 _ => unsafe { ::core::hint::unreachable_unchecked() },
151 },
152 _phantom: PhantomData,
153 }
154 }
155}
156
157impl<T> fmt::Debug for WireFlexible<'_, T>
158where
159 T: fmt::Debug,
160{
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 self.as_ref().fmt(f)
163 }
164}
165
166unsafe impl<D, T> Decode<D> for WireFlexible<'static, T>
167where
168 D: Decoder + ?Sized,
169 T: Decode<D>,
170{
171 fn decode(slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
172 munge!(let Self { mut raw, _phantom: _ } = slot);
173
174 match RawWireUnion::encoded_ordinal(raw.as_mut()) {
175 ORD_OK => RawWireUnion::decode_as::<D, T>(raw, decoder)?,
176 ORD_FRAMEWORK_ERR => RawWireUnion::decode_as::<D, WireFrameworkError>(raw, decoder)?,
177 ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
178 }
179
180 Ok(())
181 }
182}
183
184impl<T> Encodable for Flexible<T>
185where
186 T: Encodable,
187{
188 type Encoded = WireFlexible<'static, T::Encoded>;
189}
190
191unsafe impl<E, T> Encode<E> for Flexible<T>
192where
193 E: Encoder + ?Sized,
194 T: Encode<E>,
195{
196 fn encode(
197 self,
198 encoder: &mut E,
199 out: &mut MaybeUninit<Self::Encoded>,
200 ) -> Result<(), EncodeError> {
201 munge!(let WireFlexible { raw, _phantom: _ } = out);
202
203 match self {
204 Self::Ok(value) => RawWireUnion::encode_as::<E, T>(value, ORD_OK, encoder, raw)?,
205 Self::FrameworkErr(error) => RawWireUnion::encode_as::<E, FrameworkError>(
206 error,
207 ORD_FRAMEWORK_ERR,
208 encoder,
209 raw,
210 )?,
211 }
212
213 Ok(())
214 }
215}
216
217unsafe impl<E, T> EncodeRef<E> for Flexible<T>
218where
219 E: Encoder + ?Sized,
220 T: EncodeRef<E>,
221{
222 fn encode_ref(
223 &self,
224 encoder: &mut E,
225 out: &mut MaybeUninit<Self::Encoded>,
226 ) -> Result<(), EncodeError> {
227 self.as_ref().encode(encoder, out)
228 }
229}
230
231impl<T, WT> FromWire<WireFlexible<'_, WT>> for Flexible<T>
232where
233 T: FromWire<WT>,
234{
235 fn from_wire(wire: WireFlexible<'_, WT>) -> Self {
236 match wire.to_flexible() {
237 Flexible::Ok(value) => Self::Ok(T::from_wire(value)),
238 Flexible::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
239 }
240 }
241}
242
243impl<T, WT> FromWireRef<WireFlexible<'_, WT>> for Flexible<T>
244where
245 T: FromWireRef<WT>,
246{
247 fn from_wire_ref(wire: &WireFlexible<'_, WT>) -> Self {
248 match wire.as_ref() {
249 Flexible::Ok(value) => Self::Ok(T::from_wire_ref(value)),
250 Flexible::FrameworkErr(framework_error) => Self::FrameworkErr(framework_error),
251 }
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use fidl_next_codec::chunks;
258
259 use super::{Flexible, WireFlexible};
260 use crate::testing::{assert_decoded, assert_encoded};
261 use crate::FrameworkError;
262
263 #[test]
264 fn encode_flexible_result() {
265 assert_encoded(
266 Flexible::<()>::Ok(()),
267 &chunks![
268 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
269 0x01, 0x00,
270 ],
271 );
272 assert_encoded(
273 Flexible::<()>::FrameworkErr(FrameworkError::UnknownMethod),
274 &chunks![
275 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
276 0x01, 0x00,
277 ],
278 );
279 }
280
281 #[test]
282 fn decode_flexible_result() {
283 assert_decoded::<WireFlexible<'_, ()>>(
284 &mut chunks![
285 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
286 0x01, 0x00,
287 ],
288 |x| assert!(matches!(x.as_ref(), Flexible::Ok(()))),
289 );
290 assert_decoded::<WireFlexible<'_, ()>>(
291 &mut chunks![
292 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
293 0x01, 0x00,
294 ],
295 |x| {
296 assert!(matches!(x.as_ref(), Flexible::FrameworkErr(FrameworkError::UnknownMethod)))
297 },
298 );
299 }
300}