use core::borrow::Borrow;
use crate::ValueCodecIdError;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ValueCodecId(
&'static str,
);
impl ValueCodecId {
#[must_use]
pub const fn new(value: &'static str) -> Self {
match Self::try_new(value) {
Ok(id) => id,
Err(_) => panic!("invalid value codec ID"),
}
}
pub const fn try_new(value: &'static str) -> Result<Self, ValueCodecIdError> {
match validate(value) {
Ok(()) => Ok(Self(value)),
Err(error) => Err(error),
}
}
#[inline]
#[must_use]
pub const fn as_str(self) -> &'static str {
self.0
}
}
impl Borrow<str> for ValueCodecId {
#[inline]
fn borrow(&self) -> &str {
self.0
}
}
const fn validate(value: &str) -> Result<(), ValueCodecIdError> {
let bytes = value.as_bytes();
if bytes.is_empty() {
return Err(ValueCodecIdError::Empty);
}
let mut start = 0;
let mut index = 0;
while index < bytes.len() {
if bytes[index] == b'.' {
if start == index || index + 1 == bytes.len() {
return Err(ValueCodecIdError::EmptySegment);
}
if let Err(error) = validate_segment(bytes, start, index) {
return Err(error);
}
start = index + 1;
}
index += 1;
}
validate_segment(bytes, start, bytes.len())
}
const fn validate_segment(bytes: &[u8], start: usize, end: usize) -> Result<(), ValueCodecIdError> {
if start == end || !bytes[start].is_ascii_alphabetic() {
return Err(ValueCodecIdError::InvalidSegment);
}
let mut index = start + 1;
while index < end {
let byte = bytes[index];
if !(byte.is_ascii_alphanumeric() || byte == b'_') {
return Err(ValueCodecIdError::InvalidSegment);
}
index += 1;
}
Ok(())
}