use core::fmt;
use core::str::FromStr;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct DptId {
pub main: u16,
pub sub: u16,
}
impl DptId {
pub const fn new(main: u16, sub: u16) -> Self {
Self { main, sub }
}
pub fn parse(text: &str) -> Result<Self, ParseIdError> {
let Some((main, sub)) = text.split_once('.') else {
return Err(ParseIdError::NoSeparator);
};
let main = parse_component(main.as_bytes())?;
let sub = parse_component(sub.as_bytes())?;
Ok(Self { main, sub })
}
}
fn parse_component(bytes: &[u8]) -> Result<u16, ParseIdError> {
if bytes.is_empty() {
return Err(ParseIdError::EmptyComponent);
}
if !bytes.iter().all(u8::is_ascii_digit) {
return Err(ParseIdError::NonDigitByte);
}
if bytes.len() > 5 {
return Err(ParseIdError::TooManyDigits);
}
let mut value: u32 = 0;
for &byte in bytes {
value = value * 10 + u32::from(byte - b'0');
}
u16::try_from(value).map_err(|_| ParseIdError::Overflow)
}
impl FromStr for DptId {
type Err = ParseIdError;
fn from_str(text: &str) -> Result<Self, Self::Err> {
Self::parse(text)
}
}
impl fmt::Display for DptId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}.{:03}", self.main, self.sub)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ParseIdError {
NoSeparator,
EmptyComponent,
NonDigitByte,
TooManyDigits,
Overflow,
}
impl fmt::Display for ParseIdError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::NoSeparator => "identifier has no `.` separator",
Self::EmptyComponent => "identifier component is empty",
Self::NonDigitByte => "identifier component contains a non-digit byte",
Self::TooManyDigits => "identifier component has more than 5 digits",
Self::Overflow => "identifier component exceeds 65535",
})
}
}
impl core::error::Error for ParseIdError {}