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