use std::{
fmt,
hash::{Hash, Hasher},
str::FromStr,
};
use smallvec::SmallVec;
use crate::error::{Error, Result};
#[inline]
pub(crate) fn valid_prefix_char(c: u8) -> bool {
(c > b'/' && c < b':') || (c > b'`' && c < b'{') || (c > b'@' && c < b'[')
}
#[cfg(test)]
mod valid_prefix_char_tests {
use super::*;
#[test]
fn valid() {
assert!(valid_prefix_char(b'0'));
assert!(valid_prefix_char(b'9'));
assert!(valid_prefix_char(b'A'));
assert!(valid_prefix_char(b'Z'));
assert!(valid_prefix_char(b'a'));
assert!(valid_prefix_char(b'z'));
}
#[test]
fn invalid() {
assert!(!valid_prefix_char(b'/'));
assert!(!valid_prefix_char(b':'));
assert!(!valid_prefix_char(b'`'));
assert!(!valid_prefix_char(b'{'));
assert!(!valid_prefix_char(b'@'));
assert!(!valid_prefix_char(b'['));
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Prefix {
bytes: SmallVec<[u8; 8]>,
}
impl Prefix {
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if !slice.iter().all(|&c| valid_prefix_char(c)) {
return Err(Error::InvalidPrefix {
valid_until: slice
.iter()
.enumerate()
.find(|(_i, &c)| !valid_prefix_char(c))
.map(|(i, _)| i)
.unwrap(),
});
}
Ok(Self::from_slice_unchecked(slice))
}
pub fn from_slice_unchecked(slice: &[u8]) -> Self {
Self {
bytes: SmallVec::from_slice(slice),
}
}
}
impl fmt::Display for Prefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe {
write!(
f,
"{}",
std::str::from_utf8_unchecked(self.bytes.as_slice())
)
}
}
}
impl FromStr for Prefix {
type Err = Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { Self::from_slice(s.as_bytes()) }
}
impl TryFrom<&[u8]> for Prefix {
type Error = Error;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> { Self::from_slice(slice) }
}
impl TryFrom<&str> for Prefix {
type Error = Error;
fn try_from(s: &str) -> std::result::Result<Self, Self::Error> { s.parse() }
}
impl Hash for Prefix {
fn hash<H: Hasher>(&self, state: &mut H) { self.bytes.hash(state); }
}
#[cfg(test)]
mod prefix_tests {
use super::*;
use smallvec::smallvec;
#[test]
fn from_str() {
let pfx = "PFX".parse::<Prefix>();
assert!(pfx.is_ok());
assert_eq!(
pfx.unwrap(),
Prefix {
bytes: smallvec![b'P', b'F', b'X']
}
);
}
#[test]
fn from_str_err_char() {
let pfx = "PF[".parse::<Prefix>();
assert!(pfx.is_err());
assert_eq!(pfx.unwrap_err(), Error::InvalidPrefix { valid_until: 2 });
}
#[test]
fn from_str_mixedcase() {
let pfx = "PFx".parse::<Prefix>();
assert!(pfx.is_ok());
assert_eq!(
pfx.unwrap(),
Prefix {
bytes: smallvec![b'P', b'F', b'x']
}
);
}
#[test]
fn from_slice() {
let arr: [u8; 3] = [b'P', b'F', b'X'];
let pfx = Prefix::from_slice(arr.as_slice());
assert!(pfx.is_ok());
assert_eq!(
pfx.unwrap(),
Prefix {
bytes: SmallVec::from_slice(&arr)
}
);
}
#[test]
fn from_slice_err_char() {
let arr: [u8; 3] = [b'P', b'F', b']'];
let pfx = Prefix::from_slice(arr.as_slice());
assert!(pfx.is_err());
assert_eq!(pfx.unwrap_err(), Error::InvalidPrefix { valid_until: 2 });
}
#[test]
fn from_slice_mixedcase() {
let arr: [u8; 3] = [b'P', b'F', b'x'];
let pfx = Prefix::from_slice(arr.as_slice());
assert!(pfx.is_ok());
assert_eq!(
pfx.unwrap(),
Prefix {
bytes: smallvec![b'P', b'F', b'x']
}
);
}
#[test]
fn to_string() {
let pfx: Prefix = "PFx".parse().unwrap();
assert_eq!("PFx".to_string(), pfx.to_string());
}
}