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