1use std::fmt::{Display, Formatter};
2
3use crate::parse::Error;
4use crate::parse::Error::*;
5
6#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
13pub struct Fragment<'a> {
14 fragment: &'a str,
15}
16
17impl Default for Fragment<'static> {
18 fn default() -> Self {
19 Self { fragment: "#" }
20 }
21}
22
23impl<'a> Fragment<'a> {
24 pub unsafe fn new(fragment: &'a str) -> Self {
31 debug_assert!(Self::is_valid(fragment));
32
33 Self { fragment }
34 }
35}
36
37impl<'a> TryFrom<&'a str> for Fragment<'a> {
38 type Error = Error;
39
40 fn try_from(fragment: &'a str) -> Result<Self, Self::Error> {
41 if Self::is_valid(fragment) {
42 Ok(Self { fragment })
43 } else {
44 Err(InvalidFragment)
45 }
46 }
47}
48
49impl<'a> Fragment<'a> {
50 fn is_valid_char(c: u8) -> bool {
54 c.is_ascii_alphanumeric() || (c.is_ascii_punctuation())
55 }
56
57 pub fn is_valid(fragment: &str) -> bool {
59 !fragment.is_empty()
60 && fragment.as_bytes()[0] == b'#'
61 && fragment.as_bytes()[1..]
62 .iter()
63 .all(|c| Self::is_valid_char(*c))
64 }
65}
66
67impl<'a> Fragment<'a> {
68 pub fn fragment(&self) -> &str {
72 &self.fragment[1..]
73 }
74
75 pub const fn as_str(&self) -> &str {
77 self.fragment
78 }
79}
80
81impl<'a> AsRef<str> for Fragment<'a> {
82 fn as_ref(&self) -> &str {
83 self.fragment
84 }
85}
86
87impl<'a> Display for Fragment<'a> {
88 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
89 write!(f, "{}", self.fragment)
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use crate::Fragment;
96
97 #[test]
98 fn new() {
99 let fragment: Fragment = unsafe { Fragment::new("#the-fragment") };
100 assert_eq!(fragment.fragment, "#the-fragment");
101 }
102
103 #[test]
104 fn is_valid() {
105 let test_cases: &[(&str, bool)] = &[
106 ("", false),
107 ("#", true),
108 ("###", true),
109 ("#azAZ09", true),
110 ("#!/&/=/~/", true),
111 ("#?", true),
112 ("#!", true),
113 ("# ", false),
114 ("# x", false),
115 ];
116 for (fragment, expected) in test_cases {
117 let result: bool = Fragment::is_valid(fragment);
118 assert_eq!(result, *expected, "fragment={}", fragment);
119 }
120 }
121
122 #[test]
123 fn display() {
124 let fragment: Fragment = unsafe { Fragment::new("#the-fragment") };
125 assert_eq!(fragment.as_str(), "#the-fragment");
126 assert_eq!(fragment.as_ref(), "#the-fragment");
127 assert_eq!(fragment.to_string(), "#the-fragment");
128 }
129}