Skip to main content

fidl_next_codec/wire/fuchsia/
status.rs

1// Copyright 2026 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::mem::MaybeUninit;
7
8use munge::munge;
9
10use crate::{
11    Constrained, Decode, DecodeError, Encode, EncodeError, FromWire, FromWireRef, IntoNatural,
12    Slot, ValidationError, Wire, wire,
13};
14
15/// The wire type for [`Result<(), zx::Status>`].
16#[derive(Clone, Copy)]
17#[repr(transparent)]
18pub struct StatusResult {
19    inner: wire::Int32,
20}
21
22impl Constrained for StatusResult {
23    type Constraint = ();
24
25    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
26        Ok(())
27    }
28}
29
30// SAFETY:
31// - Lifetime erasure: `StatusResult` has no lifetimes, so `Narrowed` is `Self`.
32// - Padding: `StatusResult` is transparent over `Int32`, which has no padding.
33unsafe impl Wire for StatusResult {
34    type Narrowed<'de> = Self;
35
36    #[inline]
37    fn zero_padding(out: &mut MaybeUninit<Self>) {
38        munge!(let Self { inner } = out);
39        wire::Int32::zero_padding(inner);
40    }
41}
42
43impl StatusResult {
44    /// Returns the raw status code.
45    pub fn into_raw(self) -> i32 {
46        *self.inner
47    }
48
49    /// Returns a `Result<(), zx::Status>` with the same value as this wire type.
50    pub fn to_result(self) -> Result<(), zx::Status> {
51        zx::Status::ok(*self.inner)
52    }
53}
54
55impl From<zx::Status> for StatusResult {
56    fn from(value: zx::Status) -> Self {
57        Self { inner: wire::Int32(value.into_raw()) }
58    }
59}
60
61impl From<Result<(), zx::Status>> for StatusResult {
62    fn from(value: Result<(), zx::Status>) -> Self {
63        Self { inner: wire::Int32(zx::Status::result_into_raw(value)) }
64    }
65}
66
67impl fmt::Debug for StatusResult {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        self.to_result().fmt(f)
70    }
71}
72
73// SAFETY: `decode` delegates to `Int32::decode`, which initializes the underlying `Int32`
74// and ensures `slot` contains a valid decoded `StatusResult`.
75unsafe impl<D: ?Sized> Decode<D> for StatusResult {
76    fn decode(
77        slot: Slot<'_, Self>,
78        decoder: &mut D,
79        _: Self::Constraint,
80    ) -> Result<(), DecodeError> {
81        munge!(let Self { inner } = slot);
82        wire::Int32::decode(inner, decoder, ())
83    }
84}
85
86// SAFETY: `encode` delegates to the `Encode` implementation of the raw `i32` status value,
87// which initializes all non-padding bytes of `out`.
88unsafe impl<E: ?Sized> Encode<StatusResult, E> for zx::Status {
89    fn encode(
90        self,
91        encoder: &mut E,
92        out: &mut MaybeUninit<StatusResult>,
93        constraint: (),
94    ) -> Result<(), EncodeError> {
95        munge!(let StatusResult { inner } = out);
96        self.into_raw().encode(encoder, inner, constraint)
97    }
98}
99
100// SAFETY: `encode` delegates to `zx::Status`'s `Encode` implementation, which initializes
101// all non-padding bytes of `out`.
102unsafe impl<E: ?Sized> Encode<StatusResult, E> for &zx::Status {
103    fn encode(
104        self,
105        encoder: &mut E,
106        out: &mut MaybeUninit<StatusResult>,
107        constraint: (),
108    ) -> Result<(), EncodeError> {
109        Encode::encode(*self, encoder, out, constraint)
110    }
111}
112
113// SAFETY: `encode` delegates to the raw integer status encoding.
114unsafe impl<E: ?Sized> Encode<StatusResult, E> for Result<(), zx::Status> {
115    fn encode(
116        self,
117        encoder: &mut E,
118        out: &mut MaybeUninit<StatusResult>,
119        constraint: (),
120    ) -> Result<(), EncodeError> {
121        munge!(let StatusResult { inner } = out);
122        zx::Status::result_into_raw(self).encode(encoder, inner, constraint)
123    }
124}
125
126// SAFETY: delegates to value encoding.
127unsafe impl<E: ?Sized> Encode<StatusResult, E> for &Result<(), zx::Status> {
128    fn encode(
129        self,
130        encoder: &mut E,
131        out: &mut MaybeUninit<StatusResult>,
132        constraint: (),
133    ) -> Result<(), EncodeError> {
134        Encode::encode(*self, encoder, out, constraint)
135    }
136}
137
138impl FromWire<StatusResult> for Result<(), zx::Status> {
139    fn from_wire(wire: StatusResult) -> Self {
140        Self::from_wire_ref(&wire)
141    }
142}
143
144impl FromWireRef<StatusResult> for Result<(), zx::Status> {
145    fn from_wire_ref(wire: &StatusResult) -> Self {
146        zx::Status::ok(*wire.inner)
147    }
148}
149
150impl IntoNatural for StatusResult {
151    type Natural = Result<(), zx::Status>;
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use crate::CHUNK_SIZE;
158
159    #[test]
160    fn test_status_result_decode_zero() {
161        let mut buffer = [0u8; CHUNK_SIZE];
162        let mut decoder = ();
163        // SAFETY: `buffer` is sufficiently sized and aligned for `StatusResult`.
164        let mut slot = unsafe { Slot::<StatusResult>::new_unchecked(buffer.as_mut_ptr().cast()) };
165        StatusResult::decode(slot.as_mut(), &mut decoder, ())
166            .expect("failed to decode 0 as StatusResult");
167        // SAFETY: `slot` was successfully decoded and initialized.
168        let wire_result = unsafe { slot.as_ptr().cast::<StatusResult>().read() };
169        assert_eq!(wire_result.to_result(), Ok(()));
170        assert_eq!(
171            <Result<(), zx::Status> as FromWire<StatusResult>>::from_wire(wire_result),
172            Ok(())
173        );
174    }
175
176    #[test]
177    fn test_status_result_decode_error() {
178        let mut buffer = [0u8; CHUNK_SIZE];
179        let status_raw = zx::Status::NOT_SUPPORTED.into_raw();
180        buffer[..4].copy_from_slice(&status_raw.to_le_bytes());
181
182        let mut decoder = ();
183        // SAFETY: `buffer` is sufficiently sized and aligned for `StatusResult`.
184        let mut slot = unsafe { Slot::<StatusResult>::new_unchecked(buffer.as_mut_ptr().cast()) };
185        StatusResult::decode(slot.as_mut(), &mut decoder, ())
186            .expect("failed to decode error as StatusResult");
187        // SAFETY: `slot` was successfully decoded and initialized.
188        let wire_result = unsafe { slot.as_ptr().cast::<StatusResult>().read() };
189        assert_eq!(wire_result.to_result(), Err(zx::Status::NOT_SUPPORTED));
190        assert_eq!(
191            <Result<(), zx::Status> as FromWire<StatusResult>>::from_wire(wire_result),
192            Err(zx::Status::NOT_SUPPORTED)
193        );
194    }
195
196    #[test]
197    fn test_status_result_encode() {
198        let mut out = MaybeUninit::<StatusResult>::uninit();
199        let mut encoder = ();
200        Ok::<(), zx::Status>(()).encode(&mut encoder, &mut out, ()).unwrap();
201        // SAFETY: `encode` succeeded, so `out` is initialized.
202        let encoded = unsafe { out.assume_init() };
203        assert_eq!(encoded.into_raw(), 0);
204
205        let mut out = MaybeUninit::<StatusResult>::uninit();
206        let result: Result<(), zx::Status> = Err(zx::Status::NOT_FOUND);
207        result.encode(&mut encoder, &mut out, ()).unwrap();
208        // SAFETY: `encode` succeeded, so `out` is initialized.
209        let encoded = unsafe { out.assume_init() };
210        assert_eq!(encoded.into_raw(), zx::Status::NOT_FOUND.into_raw());
211    }
212}