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 [`zx::Status`].
16#[derive(Clone, Copy)]
17#[repr(transparent)]
18pub struct Status {
19    inner: wire::Int32,
20}
21
22impl Constrained for Status {
23    type Constraint = ();
24
25    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
26        Ok(())
27    }
28}
29
30// SAFETY:
31// - Lifetime erasure: `Status` has no lifetimes, so `Narrowed` is `Self`.
32// - Padding: `Status` is transparent over `Int32`, which has no padding.
33unsafe impl Wire for Status {
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 Status {
44    /// Returns the raw status code.
45    pub fn into_raw(self) -> i32 {
46        *self.inner
47    }
48
49    /// Returns a `zx::Status` with the same value as this wire type.
50    pub fn to_status(self) -> zx::Status {
51        zx::Status::try_from_raw(*self.inner).unwrap()
52    }
53}
54
55impl From<zx::Status> for Status {
56    fn from(value: zx::Status) -> Self {
57        Self { inner: wire::Int32(value.into_raw()) }
58    }
59}
60
61impl fmt::Debug for Status {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        self.to_status().fmt(f)
64    }
65}
66
67// SAFETY: `decode` delegates to `Int32::decode`, which initializes the underlying `Int32`
68// and ensures `slot` contains a valid decoded `Status`.
69unsafe impl<D: ?Sized> Decode<D> for Status {
70    fn decode(
71        slot: Slot<'_, Self>,
72        decoder: &mut D,
73        _: Self::Constraint,
74    ) -> Result<(), DecodeError> {
75        munge!(let Self { mut inner } = slot);
76        wire::Int32::decode(inner.as_mut(), decoder, ())?;
77        if *inner == 0 {
78            return Err(DecodeError::InvalidNonZeroInteger);
79        }
80        Ok(())
81    }
82}
83
84// SAFETY: `encode` delegates to the `Encode` implementation of the raw `i32` status value,
85// which initializes all non-padding bytes of `out`.
86unsafe impl<E: ?Sized> Encode<Status, E> for zx::Status {
87    fn encode(
88        self,
89        encoder: &mut E,
90        out: &mut MaybeUninit<Status>,
91        constraint: (),
92    ) -> Result<(), EncodeError> {
93        munge!(let Status { inner } = out);
94        self.into_raw().encode(encoder, inner, constraint)
95    }
96}
97
98// SAFETY: `encode` delegates to `zx::Status`'s `Encode` implementation, which initializes
99// all non-padding bytes of `out`.
100unsafe impl<E: ?Sized> Encode<Status, E> for &zx::Status {
101    fn encode(
102        self,
103        encoder: &mut E,
104        out: &mut MaybeUninit<Status>,
105        constraint: (),
106    ) -> Result<(), EncodeError> {
107        Encode::encode(*self, encoder, out, constraint)
108    }
109}
110
111impl FromWire<Status> for zx::Status {
112    fn from_wire(wire: Status) -> Self {
113        Self::from_wire_ref(&wire)
114    }
115}
116
117impl FromWireRef<Status> for zx::Status {
118    fn from_wire_ref(wire: &Status) -> Self {
119        wire.to_status()
120    }
121}
122
123impl IntoNatural for Status {
124    type Natural = zx::Status;
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use crate::CHUNK_SIZE;
131
132    #[test]
133    fn test_status_decode_zero() {
134        let mut buffer = [0u8; CHUNK_SIZE];
135        // SAFETY: `buffer` is sufficiently sized and aligned for `Status`.
136        let mut slot = unsafe { Slot::<Status>::new_unchecked(buffer.as_mut_ptr().cast()) };
137        Status::decode(slot.as_mut(), &mut (), ()).expect_err("successfully decoded 0 as Status");
138    }
139
140    #[test]
141    fn test_status_decode_error() {
142        let mut buffer = [0u8; CHUNK_SIZE];
143        let status_raw = zx::Status::NOT_SUPPORTED.into_raw();
144        buffer[..4].copy_from_slice(&status_raw.to_le_bytes());
145
146        // SAFETY: `buffer` is sufficiently sized and aligned for `Status`.
147        let mut slot = unsafe { Slot::<Status>::new_unchecked(buffer.as_mut_ptr().cast()) };
148        Status::decode(slot.as_mut(), &mut (), ()).expect("failed to decode error as Status");
149        // SAFETY: `slot` was successfully decoded and initialized.
150        let wire_result = unsafe { slot.as_ptr().cast::<Status>().read() };
151        assert_eq!(wire_result.to_status(), zx::Status::NOT_SUPPORTED);
152        assert_eq!(
153            <zx::Status as FromWire<Status>>::from_wire(wire_result),
154            zx::Status::NOT_SUPPORTED
155        );
156    }
157
158    #[test]
159    fn test_status_result_encode() {
160        let mut out = MaybeUninit::<Status>::uninit();
161        let status = zx::Status::NOT_FOUND;
162        status.encode(&mut (), &mut out, ()).unwrap();
163        // SAFETY: `encode` succeeded, so `out` is initialized.
164        let encoded = unsafe { out.assume_init() };
165        assert_eq!(encoded.into_raw(), zx::Status::NOT_FOUND.into_raw());
166    }
167}