use super::{Deserializable, LabelError, RpoDigest, SliceReader, ToString, Vec, MAX_LABEL_LEN};
pub const CONSTANT_LABEL_PARSER: LabelParser = LabelParser {
caps: true,
max_len: MAX_LABEL_LEN,
numbers_letters_underscore: true,
start_with_letter: true,
};
pub const NAMESPACE_LABEL_PARSER: LabelParser = LabelParser {
caps: false,
max_len: MAX_LABEL_LEN,
numbers_letters_underscore: true,
start_with_letter: true,
};
pub const PROCEDURE_LABEL_PARSER: LabelParser = LabelParser {
caps: false,
max_len: MAX_LABEL_LEN,
numbers_letters_underscore: true,
start_with_letter: true,
};
pub struct LabelParser {
pub caps: bool,
pub max_len: usize,
pub numbers_letters_underscore: bool,
pub start_with_letter: bool,
}
impl LabelParser {
pub fn parse_label<'a>(&'a self, label: &'a str) -> Result<&str, LabelError> {
if label.is_empty() {
return Err(LabelError::empty_label());
} else if label.len() > self.max_len {
return Err(LabelError::label_too_long(label, self.max_len));
} else if self.start_with_letter && !label.chars().next().unwrap().is_ascii_alphabetic() {
return Err(LabelError::invalid_fist_letter(label));
} else if self.numbers_letters_underscore
&& !label.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
{
return Err(LabelError::invalid_label(label));
} else if self.caps
&& !label
.chars()
.all(|c| !c.is_alphabetic() || (c.is_alphabetic() && c.is_uppercase()))
{
return Err(LabelError::must_be_uppercase(label));
}
Ok(label)
}
}
pub fn decode_hex_rpo_digest_label(s: &str) -> Result<RpoDigest, LabelError> {
debug_assert!(s.starts_with("0x"), "hex label must start with 0x");
if s.len() != 66 {
Err(LabelError::rpo_digest_hex_label_incorrect_length(s.len()))
} else {
let data: Vec<u8> = (2..s.len())
.step_by(2)
.map(|i| {
u8::from_str_radix(&s[i..i + 2], 16)
.map_err(|_| LabelError::InvalidHexCharacters(s.to_string()))
})
.collect::<Result<Vec<_>, LabelError>>()?;
let mut slice_reader = SliceReader::new(&data);
RpoDigest::read_from(&mut slice_reader)
.map_err(|_| LabelError::InvalidHexRpoDigestLabel(s.to_string()))
}
}