hayro_syntax/object/
name.rs1use crate::filter::ascii_hex::decode_hex_digit;
4use crate::object::Object;
5use crate::object::macros::object;
6use crate::reader::Reader;
7use crate::reader::{Readable, ReaderContext, Skippable};
8use crate::trivia::is_regular_character;
9use core::borrow::Borrow;
10use core::fmt::{self, Debug, Display, Formatter};
11use core::hash::{Hash, Hasher};
12use core::ops::Deref;
13use smallvec::SmallVec;
14
15#[derive(Clone)]
16enum NameInner<'a> {
17 Borrowed(&'a [u8]),
18 Owned(SmallVec<[u8; 23]>),
19}
20
21#[derive(Clone)]
23pub struct Name<'a>(NameInner<'a>);
24
25impl<'a> Deref for Name<'a> {
26 type Target = [u8];
27
28 fn deref(&self) -> &Self::Target {
29 self.as_ref()
30 }
31}
32
33impl AsRef<[u8]> for Name<'_> {
34 fn as_ref(&self) -> &[u8] {
35 match &self.0 {
36 NameInner::Borrowed(data) => data,
37 NameInner::Owned(data) => data,
38 }
39 }
40}
41
42impl Borrow<[u8]> for Name<'_> {
43 fn borrow(&self) -> &[u8] {
44 self.as_ref()
45 }
46}
47
48impl PartialEq for Name<'_> {
49 fn eq(&self, other: &Self) -> bool {
50 self.as_ref() == other.as_ref()
51 }
52}
53
54impl Eq for Name<'_> {}
55
56impl Hash for Name<'_> {
57 fn hash<H: Hasher>(&self, state: &mut H) {
58 self.as_ref().hash(state);
59 }
60}
61
62impl PartialOrd for Name<'_> {
63 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
64 Some(self.cmp(other))
65 }
66}
67
68impl Ord for Name<'_> {
69 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
70 self.as_ref().cmp(other.as_ref())
71 }
72}
73
74impl<'a> Name<'a> {
75 #[inline]
77 pub fn new(data: &'a [u8]) -> Option<Self> {
78 if !data.contains(&b'#') {
79 Some(Self::new_unescaped(data))
80 } else {
81 Self::new_escaped(data)
82 }
83 }
84
85 #[inline]
87 pub fn new_unescaped(data: &'a [u8]) -> Self {
88 Self(NameInner::Borrowed(data))
89 }
90
91 #[inline]
93 pub fn new_escaped(data: &'a [u8]) -> Option<Self> {
94 let mut result = SmallVec::new();
95 let mut r = Reader::new(data);
96
97 while let Some(b) = r.read_byte() {
98 if b == b'#' {
99 let hex = r.read_bytes(2)?;
100 result.push(decode_hex_digit(hex[0])? << 4 | decode_hex_digit(hex[1])?);
101 } else {
102 result.push(b);
103 }
104 }
105
106 Some(Self(NameInner::Owned(result)))
107 }
108
109 pub fn as_str(&self) -> &str {
113 core::str::from_utf8(self.as_ref()).unwrap_or("{non-ascii key}")
114 }
115}
116
117impl Debug for Name<'_> {
118 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
119 match core::str::from_utf8(self.as_ref()) {
120 Ok(s) => <str as Debug>::fmt(s, f),
121 Err(_) => <[u8] as Debug>::fmt(self.as_ref(), f),
122 }
123 }
124}
125
126impl Display for Name<'_> {
127 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
128 write!(f, "/{}", self.as_str())
129 }
130}
131
132object!(Name<'a>, Name);
133
134impl Skippable for Name<'_> {
135 fn skip(r: &mut Reader<'_>, _: bool) -> Option<()> {
136 skip_name_like(r, true).map(|_| ())
137 }
138}
139
140impl<'a> Readable<'a> for Name<'a> {
141 fn read(r: &mut Reader<'a>, _: &ReaderContext<'a>) -> Option<Self> {
142 let start = r.offset();
143 skip_name_like(r, true)?;
144 let end = r.offset();
145
146 let data = r.range(start + 1..end)?;
148 Self::new(data)
149 }
150}
151
152pub(crate) fn skip_name_like(r: &mut Reader<'_>, solidus: bool) -> Option<()> {
155 if solidus {
160 r.forward_tag(b"/")?;
161 r.forward_while(is_regular_character);
162 } else {
163 r.forward_while_1(is_regular_character)?;
164 }
165
166 Some(())
167}
168
169#[cfg(test)]
170mod tests {
171 use crate::object::Name;
172 use crate::reader::Reader;
173 use crate::reader::ReaderExt;
174 use std::ops::Deref;
175
176 #[test]
177 fn display() {
178 let name = Reader::new(b"/Test")
179 .read_without_context::<Name<'_>>()
180 .unwrap();
181
182 assert_eq!(format!("{name}"), "/Test");
183 }
184
185 #[test]
186 fn name_1() {
187 assert_eq!(
188 Reader::new("/".as_bytes())
189 .read_without_context::<Name<'_>>()
190 .unwrap()
191 .deref(),
192 b""
193 );
194 }
195
196 #[test]
197 fn name_2() {
198 assert!(
199 Reader::new("dfg".as_bytes())
200 .read_without_context::<Name<'_>>()
201 .is_none()
202 );
203 }
204
205 #[test]
206 fn name_3() {
207 assert!(
208 Reader::new("/AB#FG".as_bytes())
209 .read_without_context::<Name<'_>>()
210 .is_none()
211 );
212 }
213
214 #[test]
215 fn name_4() {
216 assert_eq!(
217 Reader::new("/Name1".as_bytes())
218 .read_without_context::<Name<'_>>()
219 .unwrap()
220 .deref(),
221 b"Name1"
222 );
223 }
224
225 #[test]
226 fn name_5() {
227 assert_eq!(
228 Reader::new("/ASomewhatLongerName".as_bytes())
229 .read_without_context::<Name<'_>>()
230 .unwrap()
231 .deref(),
232 b"ASomewhatLongerName"
233 );
234 }
235
236 #[test]
237 fn name_6() {
238 assert_eq!(
239 Reader::new("/A;Name_With-Various***Characters?".as_bytes())
240 .read_without_context::<Name<'_>>()
241 .unwrap()
242 .deref(),
243 b"A;Name_With-Various***Characters?"
244 );
245 }
246
247 #[test]
248 fn name_7() {
249 assert_eq!(
250 Reader::new("/1.2".as_bytes())
251 .read_without_context::<Name<'_>>()
252 .unwrap()
253 .deref(),
254 b"1.2"
255 );
256 }
257
258 #[test]
259 fn name_8() {
260 assert_eq!(
261 Reader::new("/$$".as_bytes())
262 .read_without_context::<Name<'_>>()
263 .unwrap()
264 .deref(),
265 b"$$"
266 );
267 }
268
269 #[test]
270 fn name_9() {
271 assert_eq!(
272 Reader::new("/@pattern".as_bytes())
273 .read_without_context::<Name<'_>>()
274 .unwrap()
275 .deref(),
276 b"@pattern"
277 );
278 }
279
280 #[test]
281 fn name_10() {
282 assert_eq!(
283 Reader::new("/.notdef".as_bytes())
284 .read_without_context::<Name<'_>>()
285 .unwrap()
286 .deref(),
287 b".notdef"
288 );
289 }
290
291 #[test]
292 fn name_11() {
293 assert_eq!(
294 Reader::new("/lime#20Green".as_bytes())
295 .read_without_context::<Name<'_>>()
296 .unwrap()
297 .deref(),
298 b"lime Green"
299 );
300 }
301
302 #[test]
303 fn name_12() {
304 assert_eq!(
305 Reader::new("/paired#28#29parentheses".as_bytes())
306 .read_without_context::<Name<'_>>()
307 .unwrap()
308 .deref(),
309 b"paired()parentheses"
310 );
311 }
312
313 #[test]
314 fn name_13() {
315 assert_eq!(
316 Reader::new("/The_Key_of_F#23_Minor".as_bytes())
317 .read_without_context::<Name<'_>>()
318 .unwrap()
319 .deref(),
320 b"The_Key_of_F#_Minor"
321 );
322 }
323
324 #[test]
325 fn name_14() {
326 assert_eq!(
327 Reader::new("/A#42".as_bytes())
328 .read_without_context::<Name<'_>>()
329 .unwrap()
330 .deref(),
331 b"AB"
332 );
333 }
334
335 #[test]
336 fn name_15() {
337 assert_eq!(
338 Reader::new("/A#3b".as_bytes())
339 .read_without_context::<Name<'_>>()
340 .unwrap()
341 .deref(),
342 b"A;"
343 );
344 }
345
346 #[test]
347 fn name_16() {
348 assert_eq!(
349 Reader::new("/A#3B".as_bytes())
350 .read_without_context::<Name<'_>>()
351 .unwrap()
352 .deref(),
353 b"A;"
354 );
355 }
356
357 #[test]
358 fn name_17() {
359 assert_eq!(
360 Reader::new("/k1 ".as_bytes())
361 .read_without_context::<Name<'_>>()
362 .unwrap()
363 .deref(),
364 b"k1"
365 );
366 }
367}