1use std::{
2 fmt,
3 hash::{Hash, Hasher},
4 str::FromStr,
5};
6
7use smallvec::SmallVec;
8
9use crate::error::{Error, Result};
10
11#[inline]
12pub(crate) fn valid_prefix_char(c: u8) -> bool {
13 (c > b'/' && c < b':') || (c > b'`' && c < b'{') || (c > b'@' && c < b'[')
14}
15
16#[cfg(test)]
17mod valid_prefix_char_tests {
18 use super::*;
19
20 #[test]
21 fn valid() {
22 assert!(valid_prefix_char(b'0'));
23 assert!(valid_prefix_char(b'9'));
24 assert!(valid_prefix_char(b'A'));
25 assert!(valid_prefix_char(b'Z'));
26 assert!(valid_prefix_char(b'a'));
27 assert!(valid_prefix_char(b'z'));
28 }
29
30 #[test]
31 fn invalid() {
32 assert!(!valid_prefix_char(b'/'));
33 assert!(!valid_prefix_char(b':'));
34 assert!(!valid_prefix_char(b'`'));
35 assert!(!valid_prefix_char(b'{'));
36 assert!(!valid_prefix_char(b'@'));
37 assert!(!valid_prefix_char(b'['));
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct Prefix {
48 bytes: SmallVec<[u8; 8]>,
49}
50
51impl Prefix {
52 pub fn from_slice(slice: &[u8]) -> Result<Self> {
56 if !slice.iter().all(|&c| valid_prefix_char(c)) {
58 return Err(Error::InvalidPrefix {
59 valid_until: slice
60 .iter()
61 .enumerate()
62 .find(|(_i, &c)| !valid_prefix_char(c))
63 .map(|(i, _)| i)
64 .unwrap(),
65 });
66 }
67 Ok(Self::from_slice_unchecked(slice))
68 }
69
70 pub fn from_slice_unchecked(slice: &[u8]) -> Self {
73 Self {
74 bytes: SmallVec::from_slice(slice),
75 }
76 }
77}
78
79impl fmt::Display for Prefix {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 unsafe {
85 write!(
86 f,
87 "{}",
88 std::str::from_utf8_unchecked(self.bytes.as_slice())
89 )
90 }
91 }
92}
93
94impl FromStr for Prefix {
95 type Err = Error;
96
97 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { Self::from_slice(s.as_bytes()) }
98}
99
100impl TryFrom<&[u8]> for Prefix {
101 type Error = Error;
102
103 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> { Self::from_slice(slice) }
104}
105
106impl TryFrom<&str> for Prefix {
107 type Error = Error;
108
109 fn try_from(s: &str) -> std::result::Result<Self, Self::Error> { s.parse() }
110}
111
112impl Hash for Prefix {
113 fn hash<H: Hasher>(&self, state: &mut H) { self.bytes.hash(state); }
114}
115
116#[cfg(test)]
117mod prefix_tests {
118 use super::*;
119 use smallvec::smallvec;
120
121 #[test]
122 fn from_str() {
123 let pfx = "PFX".parse::<Prefix>();
124 assert!(pfx.is_ok());
125 assert_eq!(
126 pfx.unwrap(),
127 Prefix {
128 bytes: smallvec![b'P', b'F', b'X']
129 }
130 );
131 }
132
133 #[test]
134 fn from_str_err_char() {
135 let pfx = "PF[".parse::<Prefix>();
136 assert!(pfx.is_err());
137 assert_eq!(pfx.unwrap_err(), Error::InvalidPrefix { valid_until: 2 });
138 }
139
140 #[test]
141 fn from_str_mixedcase() {
142 let pfx = "PFx".parse::<Prefix>();
143 assert!(pfx.is_ok());
144 assert_eq!(
145 pfx.unwrap(),
146 Prefix {
147 bytes: smallvec![b'P', b'F', b'x']
148 }
149 );
150 }
151
152 #[test]
153 fn from_slice() {
154 let arr: [u8; 3] = [b'P', b'F', b'X'];
155 let pfx = Prefix::from_slice(arr.as_slice());
156 assert!(pfx.is_ok());
157 assert_eq!(
158 pfx.unwrap(),
159 Prefix {
160 bytes: SmallVec::from_slice(&arr)
161 }
162 );
163 }
164
165 #[test]
166 fn from_slice_err_char() {
167 let arr: [u8; 3] = [b'P', b'F', b']'];
168 let pfx = Prefix::from_slice(arr.as_slice());
169 assert!(pfx.is_err());
170 assert_eq!(pfx.unwrap_err(), Error::InvalidPrefix { valid_until: 2 });
171 }
172
173 #[test]
174 fn from_slice_mixedcase() {
175 let arr: [u8; 3] = [b'P', b'F', b'x'];
176 let pfx = Prefix::from_slice(arr.as_slice());
177 assert!(pfx.is_ok());
178 assert_eq!(
179 pfx.unwrap(),
180 Prefix {
181 bytes: smallvec![b'P', b'F', b'x']
182 }
183 );
184 }
185
186 #[test]
187 fn to_string() {
188 let pfx: Prefix = "PFx".parse().unwrap();
189 assert_eq!("PFx".to_string(), pfx.to_string());
190 }
191}