hayro_syntax/object/
name.rs

1//! Names.
2
3use 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/// A name.
18#[derive(Debug, Clone)]
19pub struct Name<'a>(Cow<'a>);
20
21// Two names should be equal even if one is borrowed and the other is owned,
22// so we need these manual implementations.
23
24impl<'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    /// Create a new name from a sequence of bytes.
70    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                    // We already verified when skipping that it's a valid hex sequence.
90                    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    /// Create a new name from an unescaped bytes string.
104    pub(crate) const fn from_unescaped(data: &'a [u8]) -> Name<'a> {
105        Self(Cow::Borrowed(data))
106    }
107
108    /// Return a string representation of the name.
109    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
136// This method is shared by `Name` and the parser for content stream operators (which behave like
137// names, except that they aren't preceded by a solidus.
138pub(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}