ittech/data/
util.rs

1use crate::error::OutOfRangeError;
2use std::convert::TryFrom;
3use std::fmt::{self, Write};
4
5#[derive(Clone, Copy)]
6pub struct Name {
7    pub bytes: [u8; 26],
8}
9
10#[derive(Clone, Copy)]
11pub struct DosFilename {
12    pub bytes: [u8; 13],
13}
14
15#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
16pub struct RangedU8<const LOW: u8, const HIGH: u8>(u8);
17
18
19impl<const LOW: u8, const HIGH: u8> RangedU8<LOW, HIGH> {
20    pub fn as_u8(self) -> u8 {
21        self.0
22    }
23}
24
25impl<const LOW: u8, const HIGH: u8> TryFrom<u8> for RangedU8<LOW, HIGH> {
26    type Error = OutOfRangeError<LOW, HIGH>;
27
28    fn try_from(raw: u8) -> Result<Self, Self::Error> {
29        if (LOW..=HIGH).contains(&raw) {
30            Ok(RangedU8(raw))
31        } else {
32            Err(OutOfRangeError(raw))
33        }
34    }
35}
36
37impl<const LOW: u8, const HIGH: u8> From<RangedU8<LOW, HIGH>> for u8 {
38    fn from(ranged: RangedU8<LOW, HIGH>) -> u8 {
39        ranged.as_u8()
40    }
41}
42
43fn null_terminated(bytes: &[u8]) -> &[u8] {
44    let null_pos = bytes.iter()
45        .position(|&b| b == 0)
46        .unwrap_or(bytes.len());
47    &bytes[..null_pos]
48}
49
50fn debug_bytestring(bytes: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
51    f.write_char('"')?;
52    for &byte in null_terminated(bytes) {
53        if byte.is_ascii_graphic() || byte == b' ' {
54            f.write_char(byte.into())?;
55        } else {
56            write!(f, "<0x{:02X}>", byte)?;
57        }
58    }
59    f.write_char('"')
60}
61
62impl fmt::Debug for Name {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        debug_bytestring(&self.bytes, f)
65    }
66}
67
68impl fmt::Debug for DosFilename {
69    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70        debug_bytestring(&self.bytes, f)
71    }
72}
73
74impl fmt::Display for Name {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        String::from_utf8_lossy(null_terminated(&self.bytes)).fmt(f)
77    }
78}
79
80impl fmt::Display for DosFilename {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        String::from_utf8_lossy(null_terminated(&self.bytes)).fmt(f)
83    }
84}
85
86impl<const LOW: u8, const HIGH: u8> fmt::Debug for RangedU8<LOW, HIGH> {
87    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88        self.as_u8().fmt(f)
89    }
90}