use crate::{encoder::Encoder, Arc, ObjectIdentifier};
pub(crate) struct Parser {
current_arc: Arc,
encoder: Encoder,
}
impl Parser {
pub(crate) const fn parse(s: &str) -> Self {
let bytes = s.as_bytes();
const_assert!(!bytes.is_empty(), "OID string is empty");
const_assert!(
matches!(bytes[0], b'0'..=b'9'),
"OID must start with a digit"
);
let current_arc = 0;
let encoder = Encoder::new();
Self {
current_arc,
encoder,
}
.parse_bytes(bytes)
}
pub(crate) const fn finish(self) -> ObjectIdentifier {
self.encoder.finish()
}
const fn parse_bytes(mut self, bytes: &[u8]) -> Self {
match bytes {
[] => {
self.encoder = self.encoder.encode(self.current_arc);
self
}
[byte @ b'0'..=b'9', remaining @ ..] => {
let digit = byte.saturating_sub(b'0');
self.current_arc = self.current_arc * 10 + digit as Arc;
self.parse_bytes(remaining)
}
[b'.', remaining @ ..] => {
const_assert!(!remaining.is_empty(), "invalid trailing '.' in OID");
self.encoder = self.encoder.encode(self.current_arc);
self.current_arc = 0;
self.parse_bytes(remaining)
}
[byte, ..] => {
const_assert!(
matches!(byte, b'0'..=b'9' | b'.'),
"invalid character in OID"
);
self
}
}
}
}
#[cfg(test)]
mod tests {
use super::Parser;
#[test]
fn parse() {
let oid = Parser::parse("1.23.456").finish();
assert_eq!(oid, "1.23.456".parse().unwrap());
}
#[test]
#[should_panic]
fn reject_empty_string() {
Parser::parse("");
}
#[test]
#[should_panic]
fn reject_non_digits() {
Parser::parse("X");
}
#[test]
#[should_panic]
fn reject_trailing_dot() {
Parser::parse("1.23.");
}
}