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