#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ManufactureDate {
Manufactured {
week: Option<u8>,
year: u16,
},
ModelYear(u16),
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ManufacturerId(pub [u8; 3]);
impl ManufacturerId {
pub fn from_ascii(bytes: [u8; 3]) -> Option<Self> {
if bytes.iter().all(|&b| b.is_ascii_uppercase()) {
Some(Self(bytes))
} else {
None
}
}
pub fn as_str(&self) -> &str {
debug_assert!(
self.0.iter().all(|&b| b.is_ascii_uppercase()),
"ManufacturerId invariant violated: bytes must be ASCII uppercase A-Z, got {:?}",
self.0
);
core::str::from_utf8(&self.0).expect("ManufacturerId bytes must be ASCII uppercase A-Z")
}
}
impl core::fmt::Display for ManufacturerId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MonitorString(pub [u8; 13]);
impl MonitorString {
pub fn as_str(&self) -> &str {
let bytes = &self.0;
let end = bytes.iter().position(|&b| b == 0x0A).unwrap_or(bytes.len());
let trimmed = match bytes[..end].iter().rposition(|&b| b != b' ') {
Some(i) => &bytes[..=i],
None => &[][..],
};
core::str::from_utf8(trimmed).unwrap_or("")
}
}
impl core::fmt::Display for MonitorString {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.as_str())
}
}
impl core::ops::Deref for MonitorString {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(any(feature = "alloc", feature = "std"))]
use crate::alloc::string::ToString;
#[test]
fn manufacturer_id_valid_ascii_uppercase() {
let id = ManufacturerId::from_ascii(*b"DEL").unwrap();
assert_eq!(id.as_str(), "DEL");
}
#[test]
fn manufacturer_id_rejects_lowercase() {
assert!(ManufacturerId::from_ascii(*b"del").is_none());
}
#[test]
fn manufacturer_id_rejects_digit() {
assert!(ManufacturerId::from_ascii(*b"D3L").is_none());
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn manufacturer_id_display() {
let id = ManufacturerId::from_ascii(*b"SAM").unwrap();
assert_eq!(id.to_string(), "SAM");
}
#[test]
fn monitor_string_strips_terminator_and_spaces() {
let mut buf = [b' '; 13];
let name = b"DELL U2722D";
buf[..name.len()].copy_from_slice(name);
buf[name.len()] = 0x0A;
assert_eq!(MonitorString(buf).as_str(), "DELL U2722D");
}
#[test]
fn monitor_string_no_terminator_strips_trailing_spaces() {
let mut buf = [b' '; 13];
buf[..3].copy_from_slice(b"ABC");
assert_eq!(MonitorString(buf).as_str(), "ABC");
}
#[test]
fn monitor_string_all_padding_gives_empty() {
assert_eq!(MonitorString([b' '; 13]).as_str(), "");
}
#[test]
fn monitor_string_deref() {
let mut buf = [b' '; 13];
buf[..3].copy_from_slice(b"LEN");
buf[3] = 0x0A;
let ms = MonitorString(buf);
let s: &str = &ms;
assert_eq!(s, "LEN");
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn monitor_string_display() {
let mut buf = [b' '; 13];
buf[..3].copy_from_slice(b"GSM");
buf[3] = 0x0A;
assert_eq!(MonitorString(buf).to_string(), "GSM");
}
}