1use alloc::vec::Vec;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum TypeConsistencyKind {
14 DisallowTypeCoercion,
16 AllowTypeCoercion,
18 ForceTypeValidation,
20}
21
22impl TypeConsistencyKind {
23 #[must_use]
25 pub const fn to_u32(self) -> u32 {
26 match self {
27 Self::DisallowTypeCoercion => 0,
28 Self::AllowTypeCoercion => 1,
29 Self::ForceTypeValidation => 2,
30 }
31 }
32
33 #[must_use]
35 pub const fn from_u32(v: u32) -> Self {
36 match v {
37 1 => Self::AllowTypeCoercion,
38 2 => Self::ForceTypeValidation,
39 _ => Self::DisallowTypeCoercion,
40 }
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct TypeConsistencyEnforcement {
47 pub kind: TypeConsistencyKind,
49 pub ignore_sequence_bounds: bool,
51 pub ignore_string_bounds: bool,
53 pub ignore_member_names: bool,
55 pub prevent_type_widening: bool,
57 pub force_type_validation: bool,
59}
60
61impl Default for TypeConsistencyEnforcement {
62 fn default() -> Self {
63 Self {
64 kind: TypeConsistencyKind::AllowTypeCoercion,
66 ignore_sequence_bounds: true,
67 ignore_string_bounds: true,
68 ignore_member_names: false,
69 prevent_type_widening: false,
70 force_type_validation: false,
71 }
72 }
73}
74
75impl TypeConsistencyEnforcement {
76 #[must_use]
92 pub fn to_bytes_le(self) -> Vec<u8> {
93 let mut out = Vec::with_capacity(12);
94 out.extend_from_slice(&self.kind.to_u32().to_le_bytes());
95 out.push(u8::from(self.ignore_sequence_bounds));
96 out.push(u8::from(self.ignore_string_bounds));
97 out.push(u8::from(self.ignore_member_names));
98 out.push(u8::from(self.prevent_type_widening));
99 out.push(u8::from(self.force_type_validation));
100 while out.len() % 4 != 0 {
101 out.push(0); }
103 out
104 }
105
106 #[must_use]
110 pub fn from_bytes_le(bytes: &[u8]) -> Self {
111 if bytes.len() < 4 {
112 return Self::default();
113 }
114 let mut k = [0u8; 4];
116 k.copy_from_slice(&bytes[..4]);
117 let kind = TypeConsistencyKind::from_u32(u32::from_le_bytes(k));
118 Self {
119 kind,
120 ignore_sequence_bounds: bytes.get(4).copied().unwrap_or(1) != 0,
121 ignore_string_bounds: bytes.get(5).copied().unwrap_or(1) != 0,
122 ignore_member_names: bytes.get(6).copied().unwrap_or(0) != 0,
123 prevent_type_widening: bytes.get(7).copied().unwrap_or(0) != 0,
124 force_type_validation: bytes.get(8).copied().unwrap_or(0) != 0,
125 }
126 }
127}
128
129#[repr(i16)]
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum DataRepresentationId {
133 Xcdr1 = 0,
135 Xml = 1,
137 Xcdr2 = 2,
139}
140
141impl DataRepresentationId {
142 #[must_use]
144 pub const fn from_i16(v: i16) -> Option<Self> {
145 match v {
146 0 => Some(Self::Xcdr1),
147 1 => Some(Self::Xml),
148 2 => Some(Self::Xcdr2),
149 _ => None,
150 }
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum RepresentationNegotiation {
157 Accepted(DataRepresentationId),
159 NoOverlap,
161}
162
163#[must_use]
167pub fn negotiate_representation(
168 writer_offered: &[i16],
169 reader_accepted: &[i16],
170) -> RepresentationNegotiation {
171 for w in writer_offered {
172 if reader_accepted.contains(w) {
173 if let Some(kind) = DataRepresentationId::from_i16(*w) {
174 return RepresentationNegotiation::Accepted(kind);
175 }
176 }
177 }
178 RepresentationNegotiation::NoOverlap
179}
180
181pub fn check_data_repr_extensibility(
192 repr: DataRepresentationId,
193 ext: ExtensibilityForRepr,
194) -> Result<(), &'static str> {
195 use DataRepresentationId::*;
196 use ExtensibilityForRepr::*;
197 match (repr, ext) {
198 (Xcdr1, Final) | (Xcdr1, Mutable) => Ok(()),
202 (Xcdr1, Appendable) => Err(
203 "Tab.59 §7.6.3.1: XCDR1 unterstuetzt APPENDABLE nicht direkt — Encoder muss FINAL waehlen",
204 ),
205 (Xcdr2, _) => Ok(()),
207 (Xml, _) => Ok(()),
210 }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
215pub enum ExtensibilityForRepr {
216 Final,
218 Appendable,
220 Mutable,
222}
223
224#[cfg(test)]
225#[allow(clippy::unwrap_used)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn consistency_roundtrip_default() {
231 let c = TypeConsistencyEnforcement::default();
232 let bytes = c.to_bytes_le();
233 let decoded = TypeConsistencyEnforcement::from_bytes_le(&bytes);
234 assert_eq!(decoded, c);
235 }
236
237 #[test]
238 fn consistency_flags_all_set() {
239 let c = TypeConsistencyEnforcement {
240 kind: TypeConsistencyKind::ForceTypeValidation,
241 ignore_sequence_bounds: false,
242 ignore_string_bounds: false,
243 ignore_member_names: true,
244 prevent_type_widening: true,
245 force_type_validation: true,
246 };
247 let bytes = c.to_bytes_le();
248 let decoded = TypeConsistencyEnforcement::from_bytes_le(&bytes);
249 assert_eq!(decoded, c);
250 }
251
252 #[test]
253 fn negotiate_xcdr2_preferred() {
254 let result = negotiate_representation(&[2, 0], &[0, 2]);
255 assert_eq!(
256 result,
257 RepresentationNegotiation::Accepted(DataRepresentationId::Xcdr2)
258 );
259 }
260
261 #[test]
262 fn negotiate_fallback_to_xcdr1() {
263 let result = negotiate_representation(&[0], &[0, 2]);
264 assert_eq!(
265 result,
266 RepresentationNegotiation::Accepted(DataRepresentationId::Xcdr1)
267 );
268 }
269
270 #[test]
271 fn negotiate_no_overlap() {
272 let result = negotiate_representation(&[2], &[0]);
273 assert_eq!(result, RepresentationNegotiation::NoOverlap);
274 }
275
276 #[test]
279 fn xcdr1_final_combination_is_allowed() {
280 assert!(
281 check_data_repr_extensibility(DataRepresentationId::Xcdr1, ExtensibilityForRepr::Final)
282 .is_ok()
283 );
284 }
285
286 #[test]
287 fn xcdr1_mutable_combination_is_allowed() {
288 assert!(
290 check_data_repr_extensibility(
291 DataRepresentationId::Xcdr1,
292 ExtensibilityForRepr::Mutable
293 )
294 .is_ok()
295 );
296 }
297
298 #[test]
299 fn xcdr1_appendable_is_disallowed() {
300 let res = check_data_repr_extensibility(
301 DataRepresentationId::Xcdr1,
302 ExtensibilityForRepr::Appendable,
303 );
304 assert!(res.is_err());
305 }
306
307 #[test]
308 fn xcdr2_supports_all_three_extensibilities() {
309 for ext in [
310 ExtensibilityForRepr::Final,
311 ExtensibilityForRepr::Appendable,
312 ExtensibilityForRepr::Mutable,
313 ] {
314 assert!(
315 check_data_repr_extensibility(DataRepresentationId::Xcdr2, ext).is_ok(),
316 "XCDR2 + {ext:?} muss zulaessig sein"
317 );
318 }
319 }
320}