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, 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
126object!(Name<'a>, Name);
127
128impl Skippable for Name<'_> {
129 fn skip(r: &mut Reader<'_>, _: bool) -> Option<()> {
130 skip_name_like(r, true).map(|_| ())
131 }
132}
133
134impl<'a> Readable<'a> for Name<'a> {
135 fn read(r: &mut Reader<'a>, _: &ReaderContext<'a>) -> Option<Self> {
136 let start = r.offset();
137 skip_name_like(r, true)?;
138 let end = r.offset();
139
140 let data = r.range(start + 1..end)?;
142 Self::new(data)
143 }
144}
145
146pub(crate) fn skip_name_like(r: &mut Reader<'_>, solidus: bool) -> Option<()> {
149 if solidus {
154 r.forward_tag(b"/")?;
155 r.forward_while(is_regular_character);
156 } else {
157 r.forward_while_1(is_regular_character)?;
158 }
159
160 Some(())
161}
162
163#[cfg(test)]
164mod tests {
165 use crate::object::Name;
166 use crate::reader::Reader;
167 use crate::reader::ReaderExt;
168 use std::ops::Deref;
169
170 #[test]
171 fn name_1() {
172 assert_eq!(
173 Reader::new("/".as_bytes())
174 .read_without_context::<Name<'_>>()
175 .unwrap()
176 .deref(),
177 b""
178 );
179 }
180
181 #[test]
182 fn name_2() {
183 assert!(
184 Reader::new("dfg".as_bytes())
185 .read_without_context::<Name<'_>>()
186 .is_none()
187 );
188 }
189
190 #[test]
191 fn name_3() {
192 assert!(
193 Reader::new("/AB#FG".as_bytes())
194 .read_without_context::<Name<'_>>()
195 .is_none()
196 );
197 }
198
199 #[test]
200 fn name_4() {
201 assert_eq!(
202 Reader::new("/Name1".as_bytes())
203 .read_without_context::<Name<'_>>()
204 .unwrap()
205 .deref(),
206 b"Name1"
207 );
208 }
209
210 #[test]
211 fn name_5() {
212 assert_eq!(
213 Reader::new("/ASomewhatLongerName".as_bytes())
214 .read_without_context::<Name<'_>>()
215 .unwrap()
216 .deref(),
217 b"ASomewhatLongerName"
218 );
219 }
220
221 #[test]
222 fn name_6() {
223 assert_eq!(
224 Reader::new("/A;Name_With-Various***Characters?".as_bytes())
225 .read_without_context::<Name<'_>>()
226 .unwrap()
227 .deref(),
228 b"A;Name_With-Various***Characters?"
229 );
230 }
231
232 #[test]
233 fn name_7() {
234 assert_eq!(
235 Reader::new("/1.2".as_bytes())
236 .read_without_context::<Name<'_>>()
237 .unwrap()
238 .deref(),
239 b"1.2"
240 );
241 }
242
243 #[test]
244 fn name_8() {
245 assert_eq!(
246 Reader::new("/$$".as_bytes())
247 .read_without_context::<Name<'_>>()
248 .unwrap()
249 .deref(),
250 b"$$"
251 );
252 }
253
254 #[test]
255 fn name_9() {
256 assert_eq!(
257 Reader::new("/@pattern".as_bytes())
258 .read_without_context::<Name<'_>>()
259 .unwrap()
260 .deref(),
261 b"@pattern"
262 );
263 }
264
265 #[test]
266 fn name_10() {
267 assert_eq!(
268 Reader::new("/.notdef".as_bytes())
269 .read_without_context::<Name<'_>>()
270 .unwrap()
271 .deref(),
272 b".notdef"
273 );
274 }
275
276 #[test]
277 fn name_11() {
278 assert_eq!(
279 Reader::new("/lime#20Green".as_bytes())
280 .read_without_context::<Name<'_>>()
281 .unwrap()
282 .deref(),
283 b"lime Green"
284 );
285 }
286
287 #[test]
288 fn name_12() {
289 assert_eq!(
290 Reader::new("/paired#28#29parentheses".as_bytes())
291 .read_without_context::<Name<'_>>()
292 .unwrap()
293 .deref(),
294 b"paired()parentheses"
295 );
296 }
297
298 #[test]
299 fn name_13() {
300 assert_eq!(
301 Reader::new("/The_Key_of_F#23_Minor".as_bytes())
302 .read_without_context::<Name<'_>>()
303 .unwrap()
304 .deref(),
305 b"The_Key_of_F#_Minor"
306 );
307 }
308
309 #[test]
310 fn name_14() {
311 assert_eq!(
312 Reader::new("/A#42".as_bytes())
313 .read_without_context::<Name<'_>>()
314 .unwrap()
315 .deref(),
316 b"AB"
317 );
318 }
319
320 #[test]
321 fn name_15() {
322 assert_eq!(
323 Reader::new("/A#3b".as_bytes())
324 .read_without_context::<Name<'_>>()
325 .unwrap()
326 .deref(),
327 b"A;"
328 );
329 }
330
331 #[test]
332 fn name_16() {
333 assert_eq!(
334 Reader::new("/A#3B".as_bytes())
335 .read_without_context::<Name<'_>>()
336 .unwrap()
337 .deref(),
338 b"A;"
339 );
340 }
341
342 #[test]
343 fn name_17() {
344 assert_eq!(
345 Reader::new("/k1 ".as_bytes())
346 .read_without_context::<Name<'_>>()
347 .unwrap()
348 .deref(),
349 b"k1"
350 );
351 }
352}