use crate::parse::Error;
use crate::parse::Error::*;
use std::fmt::{Display, Formatter};
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct Fragment<'a> {
fragment: &'a str,
}
impl Default for Fragment<'static> {
fn default() -> Self {
Self { fragment: "#" }
}
}
impl<'a> Fragment<'a> {
pub unsafe fn new(fragment: &'a str) -> Self {
debug_assert!(Self::is_valid(fragment));
Self { fragment }
}
}
impl<'a> TryFrom<&'a str> for Fragment<'a> {
type Error = Error;
fn try_from(fragment: &'a str) -> Result<Self, Self::Error> {
if Self::is_valid(fragment) {
Ok(Self { fragment })
} else {
Err(InvalidFragment)
}
}
}
impl<'a> Fragment<'a> {
fn is_valid_char(c: u8) -> bool {
c.is_ascii_alphanumeric() || c.is_ascii_punctuation()
}
pub fn is_valid(fragment: &str) -> bool {
!fragment.is_empty()
&& fragment.as_bytes()[0] == b'#'
&& fragment.as_bytes()[1..]
.iter()
.all(|c| Self::is_valid_char(*c))
}
}
impl<'a> Fragment<'a> {
pub fn fragment(&self) -> &str {
&self.fragment[1..]
}
pub const fn as_str(&self) -> &str {
self.fragment
}
}
impl<'a> AsRef<str> for Fragment<'a> {
fn as_ref(&self) -> &str {
self.fragment
}
}
impl<'a> Display for Fragment<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.fragment)
}
}
#[cfg(test)]
mod tests {
use crate::parse::Error::InvalidFragment;
use crate::Fragment;
#[test]
fn new() {
let fragment: Fragment = unsafe { Fragment::new("#the-fragment") };
assert_eq!(fragment.fragment, "#the-fragment");
}
#[test]
fn default() {
let fragment: Fragment = Fragment::default();
assert_eq!(fragment.as_str(), "#");
assert_eq!(fragment.fragment(), "");
}
#[test]
fn try_from_str() {
assert_eq!(Fragment::try_from("#").unwrap().as_str(), "#");
assert_eq!(Fragment::try_from("#section").unwrap().as_str(), "#section");
assert_eq!(Fragment::try_from(""), Err(InvalidFragment));
assert_eq!(Fragment::try_from("no-hash"), Err(InvalidFragment));
assert_eq!(Fragment::try_from("# space"), Err(InvalidFragment));
}
#[test]
fn fragment_without_hash() {
let fragment: Fragment = unsafe { Fragment::new("#the-fragment") };
assert_eq!(fragment.fragment(), "the-fragment");
let fragment: Fragment = unsafe { Fragment::new("#") };
assert_eq!(fragment.fragment(), "");
}
#[test]
fn is_valid() {
let test_cases: &[(&str, bool)] = &[
("", false),
("#", true),
("###", true),
("#azAZ09", true),
("#!/&/=/~/", true),
("#?", true),
("#!", true),
("# ", false),
("# x", false),
];
for (fragment, expected) in test_cases {
let result: bool = Fragment::is_valid(fragment);
assert_eq!(result, *expected, "fragment={}", fragment);
}
}
#[test]
fn display() {
let fragment: Fragment = unsafe { Fragment::new("#the-fragment") };
assert_eq!(fragment.as_str(), "#the-fragment");
assert_eq!(fragment.as_ref(), "#the-fragment");
assert_eq!(fragment.to_string(), "#the-fragment");
}
}