hdf5_pure/
message_type.rs1use core::fmt;
10
11#[repr(transparent)]
25#[derive(Clone, Copy, PartialEq, Eq, Hash)]
26pub struct MessageType(u16);
27
28macro_rules! message_types {
29 (
30 $(
31 $(#[$meta:meta])*
32 $constant:ident / $compat:ident = $value:literal => $display:literal;
33 )+
34 ) => {
35 impl MessageType {
36 $(
37 $(#[$meta])*
38 pub const $constant: Self = Self($value);
39 )+
40
41 #[inline]
42 const fn display_name(self) -> Option<&'static str> {
43 match self {
44 $(
45 Self::$constant => Some($display),
46 )+
47 _ => None,
48 }
49 }
50
51 #[inline]
57 pub const fn is_known(self) -> bool {
58 matches!(self, $(Self::$constant)|+)
59 }
60 }
61
62 impl fmt::Debug for MessageType {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match *self {
65 $(
66 Self::$constant => f.write_str(stringify!($compat)),
67 )+
68 other => f.debug_tuple("Unknown").field(&other.0).finish(),
69 }
70 }
71 }
72
73 #[allow(dead_code)]
76 #[repr(u16)]
77 enum KnownMessageTypeDiscriminant {
78 $(
79 $compat = $value,
80 )+
81 }
82
83 #[allow(non_upper_case_globals)]
84 impl MessageType {
85 $(
86 #[doc(hidden)]
87 #[deprecated(note = "use the uppercase MessageType associated constant")]
88 $(#[$meta])*
89 pub const $compat: Self = Self::$constant;
90 )+
91 }
92
93 #[cfg(test)]
94 const KNOWN: &[(u16, MessageType, &'static str, &'static str)] = &[
95 $(
96 (
97 $value,
98 MessageType::$constant,
99 $display,
100 stringify!($compat),
101 ),
102 )+
103 ];
104 };
105}
106
107message_types! {
108 NIL / Nil = 0x0000 => "NIL";
110
111 DATASPACE / Dataspace = 0x0001 => "dataspace";
113
114 LINK_INFO / LinkInfo = 0x0002 => "link info";
116
117 DATATYPE / Datatype = 0x0003 => "datatype";
119
120 FILL_VALUE_OLD / FillValueOld = 0x0004 => "fill value (old)";
122
123 FILL_VALUE / FillValue = 0x0005 => "fill value";
125
126 LINK / Link = 0x0006 => "link";
128
129 EXTERNAL_DATA_FILES / ExternalDataFiles = 0x0007 => "external data files";
140
141 DATA_LAYOUT / DataLayout = 0x0008 => "data layout";
143
144 GROUP_INFO / GroupInfo = 0x000A => "group info";
149
150 FILTER_PIPELINE / FilterPipeline = 0x000B => "filter pipeline";
152
153 ATTRIBUTE / Attribute = 0x000C => "attribute";
155
156 SHARED_MESSAGE_TABLE / SharedMessageTable = 0x000F => "shared message table";
162
163 OBJECT_HEADER_CONTINUATION / ObjectHeaderContinuation =
165 0x0010 => "object header continuation";
166
167 SYMBOL_TABLE / SymbolTable = 0x0011 => "symbol table";
169
170 OBJECT_MODIFICATION_TIME / ObjectModificationTime =
172 0x0012 => "object modification time";
173
174 B_TREE_K_VALUES / BTreeKValues = 0x0013 => "B-tree K values";
176
177 ATTRIBUTE_INFO / AttributeInfo = 0x0015 => "attribute info";
182
183 OBJECT_REFERENCE_COUNT / ObjectReferenceCount =
185 0x0016 => "object reference count";
186
187 FILE_SPACE_INFO / FileSpaceInfo = 0x0017 => "file space info";
189}
190
191impl MessageType {
192 #[inline]
197 pub const fn from_u16(value: u16) -> Self {
198 Self(value)
199 }
200
201 #[inline]
203 pub const fn to_u16(self) -> u16 {
204 self.0
205 }
206
207 #[inline]
212 pub const fn is_unknown(self) -> bool {
213 !self.is_known()
214 }
215
216 #[inline]
220 pub const fn unknown_id(self) -> Option<u16> {
221 if self.is_known() { None } else { Some(self.0) }
222 }
223
224 #[doc(hidden)]
229 #[deprecated(note = "use MessageType::from_u16")]
230 #[allow(non_snake_case)]
231 #[inline]
232 pub const fn Unknown(value: u16) -> Self {
233 Self(value)
234 }
235}
236
237impl From<u16> for MessageType {
238 #[inline]
239 fn from(value: u16) -> Self {
240 Self(value)
241 }
242}
243
244impl From<MessageType> for u16 {
245 #[inline]
246 fn from(value: MessageType) -> Self {
247 value.0
248 }
249}
250
251impl fmt::Display for MessageType {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 match self.display_name() {
254 Some(name) => f.write_str(name),
255 None => write!(f, "unknown message 0x{:04x}", self.0),
256 }
257 }
258}
259
260const _: () = {
263 assert!(core::mem::size_of::<MessageType>() == core::mem::size_of::<u16>());
264 assert!(core::mem::align_of::<MessageType>() == core::mem::align_of::<u16>());
265};
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn representation_matches_u16() {
273 assert_eq!(
274 core::mem::size_of::<MessageType>(),
275 core::mem::size_of::<u16>()
276 );
277 assert_eq!(
278 core::mem::align_of::<MessageType>(),
279 core::mem::align_of::<u16>()
280 );
281 }
282
283 #[test]
284 fn every_raw_identifier_roundtrips() {
285 for raw in u16::MIN..=u16::MAX {
286 let message = MessageType::from_u16(raw);
287
288 assert_eq!(message.to_u16(), raw);
289 assert_eq!(u16::from(MessageType::from(raw)), raw);
290 }
291 }
292
293 #[test]
294 fn known_identifiers_are_strictly_increasing() {
295 for pair in KNOWN.windows(2) {
296 let previous = pair[0].0;
297 let next = pair[1].0;
298
299 assert!(previous < next, "{previous:#06x} must precede {next:#06x}");
300 }
301 }
302
303 #[test]
304 fn known_types_roundtrip() {
305 for &(raw, expected, display, _) in KNOWN {
306 let message = MessageType::from_u16(raw);
307
308 assert_eq!(message, expected);
309 assert_eq!(message.to_u16(), raw);
310 assert!(message.is_known());
311 assert!(!message.is_unknown());
312 assert_eq!(message.unknown_id(), None);
313 assert_eq!(message.display_name(), Some(display));
314 }
315 }
316
317 #[test]
318 fn unknown_type_preserves_its_identifier() {
319 let message = MessageType::from_u16(0x00FF);
320
321 assert_eq!(message.to_u16(), 0x00FF);
322 assert!(!message.is_known());
323 assert!(message.is_unknown());
324 assert_eq!(message.unknown_id(), Some(0x00FF));
325 assert_eq!(message.display_name(), None);
326 }
327
328 #[test]
329 fn bogus_message_is_not_recognized() {
330 let message = MessageType::from_u16(0x0009);
331
332 assert!(message.is_unknown());
333 assert_eq!(message.unknown_id(), Some(0x0009));
334 }
335
336 #[test]
337 fn defined_but_unsupported_types_are_not_recognized() {
338 for raw in [0x000D, 0x000E, 0x0014] {
339 let message = MessageType::from_u16(raw);
340
341 assert!(message.is_unknown(), "{raw:#06x}");
342 assert_eq!(message.unknown_id(), Some(raw));
343 assert_eq!(message.to_u16(), raw);
344 }
345 }
346
347 #[test]
348 fn external_data_files_is_recognized() {
349 let message = MessageType::from_u16(0x0007);
350
351 assert_eq!(message, MessageType::EXTERNAL_DATA_FILES);
352 assert_eq!(message.to_u16(), 0x0007);
353 assert_eq!(message.display_name(), Some("external data files"));
354 }
355
356 #[test]
357 fn from_conversions_are_lossless() {
358 let message = MessageType::from(0x1234);
359
360 assert_eq!(message.to_u16(), 0x1234);
361 assert_eq!(u16::from(message), 0x1234);
362 }
363
364 #[test]
365 fn camel_case_constants_work_as_patterns() {
366 let message = MessageType::from_u16(0x0001);
367
368 assert!(matches!(message, MessageType::DATASPACE));
369 }
370
371 #[test]
372 fn unknown_constructor_preserves_the_identifier() {
373 assert_eq!(MessageType::from(0x00FF), MessageType::from_u16(0x00FF));
374 }
375}
376
377#[cfg(all(test, feature = "std"))]
378mod display_tests {
379 use super::*;
380
381 #[test]
382 fn every_known_message_uses_its_format_name() {
383 for &(raw, message, display, _) in KNOWN {
384 assert_eq!(message.to_string(), display, "{raw:#06x}");
385 }
386 }
387
388 #[test]
389 fn an_unknown_message_reports_its_raw_identifier() {
390 assert_eq!(
391 MessageType::from_u16(0x00FF).to_string(),
392 "unknown message 0x00ff"
393 );
394 }
395
396 #[test]
397 fn every_known_message_uses_its_symbolic_debug_name() {
398 for &(raw, message, _, debug) in KNOWN {
399 assert_eq!(format!("{message:?}"), debug, "{raw:#06x}");
400 }
401 }
402
403 #[test]
404 fn an_unknown_message_uses_the_unknown_debug_form() {
405 assert_eq!(
406 format!("{:?}", MessageType::from_u16(0x00FF)),
407 "Unknown(255)"
408 );
409 }
410}