Skip to main content

zerodds_xrce/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! XRCE-Wire-Format-Fehler.
5//!
6//! Analog zu `zerodds_rtps::WireError`, aber mit XRCE-spezifischen
7//! Varianten (z.B. fuer fehlende ClientKey-Praesenz).
8
9use core::fmt;
10
11/// Fehler beim Encodieren oder Decodieren von XRCE-Wire-Daten.
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum XrceError {
15    /// Ausgabe-Buffer zu klein.
16    WriteOverflow {
17        /// Wieviele Bytes gebraucht worden waeren.
18        needed: usize,
19        /// Wieviele tatsaechlich verfuegbar waren.
20        available: usize,
21    },
22    /// Eingabe endete vor dem erwarteten Ende.
23    UnexpectedEof {
24        /// Wieviele Bytes noch erwartet wurden.
25        needed: usize,
26        /// Position im Stream zum Zeitpunkt des Fehlers.
27        offset: usize,
28    },
29    /// Submessage-ID ist nicht in 0..=15 (Spec §8.3.5).
30    UnknownSubmessageId {
31        /// Roher Submessage-ID-Byte.
32        id: u8,
33    },
34    /// SubmessageHeader-Length deutet auf Body, der ueber das Datagram-
35    /// Ende hinaus reichen wuerde (Truncation).
36    TruncatedSubmessageBody {
37        /// Header-Wert `submessage_length`.
38        declared: u16,
39        /// Tatsaechlich verfuegbare Bytes ab Body-Start.
40        available: usize,
41    },
42    /// Anzahl Submessages in einer Message ueberschreitet
43    /// `DOSC_MAX_SUBMESSAGES` — DoS-Schutz.
44    TooManySubmessages {
45        /// Festes Limit.
46        limit: usize,
47    },
48    /// Payload-Groesse ueberschreitet `DOSC_MAX_PAYLOAD_SIZE` —
49    /// DoS-Schutz.
50    PayloadTooLarge {
51        /// Limit.
52        limit: usize,
53        /// Tatsaechliche Groesse.
54        actual: usize,
55    },
56    /// SubmessageLength ist nicht zum 4-Byte-Alignment passend
57    /// (§8.3.3: Offset jeder Submessage muss multiple von 4 sein).
58    UnalignedSubmessage {
59        /// Offset, an dem das Problem auftrat.
60        offset: usize,
61    },
62    /// Numerischer Wert ueberschreitet das Wire-Feld.
63    ValueOutOfRange {
64        /// Beschreibung.
65        message: &'static str,
66    },
67}
68
69impl fmt::Display for XrceError {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        match self {
72            Self::WriteOverflow { needed, available } => write!(
73                f,
74                "xrce write overflow: needed {needed}, available {available}"
75            ),
76            Self::UnexpectedEof { needed, offset } => {
77                write!(f, "xrce unexpected EOF: needed {needed} at offset {offset}")
78            }
79            Self::UnknownSubmessageId { id } => {
80                write!(f, "xrce unknown submessage id 0x{id:02x}")
81            }
82            Self::TruncatedSubmessageBody {
83                declared,
84                available,
85            } => write!(
86                f,
87                "xrce submessage body truncated: declared {declared}, available {available}"
88            ),
89            Self::TooManySubmessages { limit } => {
90                write!(f, "xrce too many submessages: limit {limit}")
91            }
92            Self::PayloadTooLarge { limit, actual } => {
93                write!(f, "xrce payload too large: limit {limit}, actual {actual}")
94            }
95            Self::UnalignedSubmessage { offset } => {
96                write!(f, "xrce submessage not 4-byte aligned at offset {offset}")
97            }
98            Self::ValueOutOfRange { message } => {
99                write!(f, "xrce value out of range: {message}")
100            }
101        }
102    }
103}
104
105#[cfg(feature = "std")]
106impl std::error::Error for XrceError {}
107
108#[cfg(test)]
109mod tests {
110    #![allow(clippy::expect_used, clippy::unwrap_used)]
111    use super::*;
112
113    #[cfg(feature = "alloc")]
114    extern crate alloc;
115    #[cfg(feature = "alloc")]
116    use alloc::format;
117
118    #[cfg(feature = "alloc")]
119    #[test]
120    fn write_overflow_display_includes_numbers() {
121        let e = XrceError::WriteOverflow {
122            needed: 16,
123            available: 4,
124        };
125        let s = format!("{e}");
126        assert!(s.contains("16") && s.contains("4"));
127    }
128
129    #[cfg(feature = "alloc")]
130    #[test]
131    fn unknown_submessage_id_display_uses_hex() {
132        let e = XrceError::UnknownSubmessageId { id: 0xAB };
133        let s = format!("{e}");
134        assert!(s.contains("ab"));
135    }
136
137    #[cfg(feature = "alloc")]
138    #[test]
139    fn truncated_body_display_includes_values() {
140        let e = XrceError::TruncatedSubmessageBody {
141            declared: 100,
142            available: 50,
143        };
144        let s = format!("{e}");
145        assert!(s.contains("100") && s.contains("50"));
146    }
147
148    #[cfg(feature = "alloc")]
149    #[test]
150    fn too_many_submessages_display_mentions_limit() {
151        let e = XrceError::TooManySubmessages { limit: 64 };
152        assert!(format!("{e}").contains("64"));
153    }
154
155    #[cfg(feature = "alloc")]
156    #[test]
157    fn payload_too_large_display_includes_both() {
158        let e = XrceError::PayloadTooLarge {
159            limit: 1024,
160            actual: 2048,
161        };
162        let s = format!("{e}");
163        assert!(s.contains("1024") && s.contains("2048"));
164    }
165
166    #[cfg(feature = "alloc")]
167    #[test]
168    fn unaligned_display_mentions_offset() {
169        let e = XrceError::UnalignedSubmessage { offset: 17 };
170        assert!(format!("{e}").contains("17"));
171    }
172}