Skip to main content

fidl_next_codec/wire/
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 crate::{
10    Chunk, Constrained, Decode, DecodeError, Decoder, Encode, EncodeError, Encoder, FromWire,
11    FromWireRef, IntoNatural, Slot, ValidationError, Wire, munge, wire,
12};
13
14use core::result::Result as CoreResult;
15
16/// A FIDL result union.
17#[repr(transparent)]
18pub struct Result<'de, T, E> {
19    raw: wire::Union,
20    _phantom: PhantomData<(&'de mut [Chunk], T, E)>,
21}
22
23impl<T, E> Drop for Result<'_, 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            _ => unsafe { ::core::hint::unreachable_unchecked() },
33        }
34    }
35}
36
37impl<T, E> Constrained for Result<'_, T, E>
38where
39    T: Constrained<Constraint = ()>,
40    E: Constrained<Constraint = ()>,
41{
42    type Constraint = ();
43
44    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> CoreResult<(), ValidationError> {
45        Ok(())
46    }
47}
48
49unsafe impl<T, E> Wire for Result<'static, T, E>
50where
51    T: Wire<Constraint = ()>,
52    E: Wire<Constraint = ()>,
53{
54    type Narrowed<'de> = Result<'de, T::Narrowed<'de>, E::Narrowed<'de>>;
55
56    #[inline]
57    fn zero_padding(out: &mut MaybeUninit<Self>) {
58        munge!(let Self { raw, _phantom: _ } = out);
59        wire::Union::zero_padding(raw);
60    }
61}
62
63const ORD_OK: u64 = 1;
64const ORD_ERR: u64 = 2;
65
66impl<T, E> Result<'_, T, E> {
67    /// Returns whether the result is `Ok`.
68    pub fn is_ok(&self) -> bool {
69        self.raw.ordinal() == ORD_OK
70    }
71
72    /// Returns whether the result is `Err`.
73    pub fn is_err(&self) -> bool {
74        self.raw.ordinal() == ORD_ERR
75    }
76
77    /// Returns the `Ok` value of the result, if any.
78    pub fn ok(&self) -> Option<&T> {
79        self.is_ok().then(|| unsafe { self.raw.get().deref_unchecked() })
80    }
81
82    /// Returns the `Err` value of the result, if any.
83    pub fn err(&self) -> Option<&E> {
84        self.is_err().then(|| unsafe { self.raw.get().deref_unchecked() })
85    }
86
87    /// Returns the contained `Ok` value.
88    ///
89    /// Panics if the result was not `Ok`.
90    pub fn unwrap(&self) -> &T {
91        self.ok().unwrap()
92    }
93
94    /// Returns the contained `Err` value.
95    ///
96    /// Panics if the result was not `Err`.
97    pub fn unwrap_err(&self) -> &E {
98        self.err().unwrap()
99    }
100
101    /// Returns a `Result` of a reference to the value or error.
102    pub fn as_ref(&self) -> CoreResult<&T, &E> {
103        match self.raw.ordinal() {
104            ORD_OK => unsafe { Ok(self.raw.get().deref_unchecked()) },
105            ORD_ERR => unsafe { Err(self.raw.get().deref_unchecked()) },
106            _ => unsafe { ::core::hint::unreachable_unchecked() },
107        }
108    }
109
110    /// Returns a `Result` of the owned value or error.
111    pub fn into_result(self) -> CoreResult<T, E> {
112        let this = ManuallyDrop::new(self);
113        match this.raw.ordinal() {
114            ORD_OK => unsafe { Ok(this.raw.get().read_unchecked()) },
115            ORD_ERR => unsafe { Err(this.raw.get().read_unchecked()) },
116            _ => unsafe { ::core::hint::unreachable_unchecked() },
117        }
118    }
119}
120
121impl<T: Clone, E: Clone> Clone for Result<'_, T, E> {
122    fn clone(&self) -> Self {
123        Self {
124            raw: match self.raw.ordinal() {
125                ORD_OK => unsafe { self.raw.clone_inline_unchecked::<T>() },
126                ORD_ERR => unsafe { self.raw.clone_inline_unchecked::<E>() },
127                _ => unsafe { ::core::hint::unreachable_unchecked() },
128            },
129            _phantom: PhantomData,
130        }
131    }
132}
133
134impl<T, E> fmt::Debug for Result<'_, T, E>
135where
136    T: fmt::Debug,
137    E: fmt::Debug,
138{
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        self.as_ref().fmt(f)
141    }
142}
143
144unsafe impl<'de, D, T, E> Decode<D> for Result<'de, T, E>
145where
146    D: Decoder<'de> + ?Sized,
147    T: Decode<D, Constraint = ()>,
148    E: Decode<D, Constraint = ()>,
149{
150    fn decode(slot: Slot<'_, Self>, decoder: &mut D, _: ()) -> CoreResult<(), DecodeError> {
151        munge!(let Self { mut raw, _phantom: _ } = slot);
152
153        match wire::Union::encoded_ordinal(raw.as_mut()) {
154            ORD_OK => wire::Union::decode_as::<D, T>(raw, decoder, ())?,
155            ORD_ERR => wire::Union::decode_as::<D, E>(raw, decoder, ())?,
156            ord => return Err(DecodeError::InvalidUnionOrdinal(ord as usize)),
157        }
158
159        Ok(())
160    }
161}
162
163unsafe impl<Enc, WT, T, WE, E> Encode<Result<'static, WT, WE>, Enc> for CoreResult<T, E>
164where
165    Enc: Encoder + ?Sized,
166    WT: Wire<Constraint = ()>,
167    T: Encode<WT, Enc>,
168    WE: Wire<Constraint = ()>,
169    E: Encode<WE, Enc>,
170{
171    fn encode(
172        self,
173        encoder: &mut Enc,
174        out: &mut MaybeUninit<Result<'_, WT, WE>>,
175        _: (),
176    ) -> CoreResult<(), EncodeError> {
177        munge!(let Result { raw, _phantom: _ } = out);
178
179        match self {
180            Ok(value) => wire::Union::encode_as::<Enc, WT>(value, ORD_OK, encoder, raw, ())?,
181            Err(error) => wire::Union::encode_as::<Enc, WE>(error, ORD_ERR, encoder, raw, ())?,
182        }
183
184        Ok(())
185    }
186}
187
188unsafe impl<'a, Enc, WT, T, WE, E> Encode<Result<'static, WT, WE>, Enc> for &'a CoreResult<T, E>
189where
190    Enc: Encoder + ?Sized,
191    WT: Wire<Constraint = ()>,
192    &'a T: Encode<WT, Enc>,
193    WE: Wire<Constraint = ()>,
194    &'a E: Encode<WE, Enc>,
195{
196    fn encode(
197        self,
198        encoder: &mut Enc,
199        out: &mut MaybeUninit<Result<'static, WT, WE>>,
200        _: (),
201    ) -> CoreResult<(), EncodeError> {
202        self.as_ref().encode(encoder, out, ())
203    }
204}
205
206impl<T, E, WT, WE> FromWire<Result<'_, WT, WE>> for CoreResult<T, E>
207where
208    T: FromWire<WT>,
209    E: FromWire<WE>,
210{
211    #[inline]
212    fn from_wire(wire: Result<'_, WT, WE>) -> Self {
213        match wire.into_result() {
214            Ok(value) => Ok(T::from_wire(value)),
215            Err(error) => Err(E::from_wire(error)),
216        }
217    }
218}
219
220impl<T: IntoNatural, E: IntoNatural> IntoNatural for Result<'_, T, E> {
221    type Natural = CoreResult<T::Natural, E::Natural>;
222}
223
224impl<T, E, WT, WE> FromWireRef<Result<'_, WT, WE>> for CoreResult<T, E>
225where
226    T: FromWireRef<WT>,
227    E: FromWireRef<WE>,
228{
229    #[inline]
230    fn from_wire_ref(wire: &Result<'_, WT, WE>) -> Self {
231        match wire.as_ref() {
232            Ok(value) => Ok(T::from_wire_ref(value)),
233            Err(error) => Err(E::from_wire_ref(error)),
234        }
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use crate::{DecoderExt as _, EncoderExt as _, chunks, wire};
241
242    #[test]
243    fn encode_result() {
244        assert_eq!(
245            Vec::encode(Result::<i32, i32>::Ok(0x12345678)).unwrap(),
246            chunks![
247                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
248                0x01, 0x00,
249            ],
250        );
251        assert_eq!(
252            Vec::encode(Result::<i32, i32>::Err(0x12345678)).unwrap(),
253            chunks![
254                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
255                0x01, 0x00,
256            ],
257        );
258    }
259
260    #[test]
261    fn decode_result() {
262        assert_eq!(
263            chunks![
264                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
265                0x01, 0x00,
266            ]
267            .as_mut_slice()
268            .decode::<wire::Result<'_, wire::Int32, wire::Int32>>()
269            .unwrap()
270            .as_ref()
271            .unwrap()
272            .0,
273            0x12345678,
274        );
275        assert_eq!(
276            chunks![
277                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
278                0x01, 0x00,
279            ]
280            .as_mut_slice()
281            .decode::<wire::Result<'_, wire::Int32, wire::Int32>>()
282            .unwrap()
283            .as_ref()
284            .unwrap_err()
285            .0,
286            0x12345678,
287        );
288    }
289}