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