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