1 | //! Code for parsing resource management things |
2 | |
3 | use super::{Binding, Component, Entry}; |
4 | use alloc::string::{String, ToString}; |
5 | use alloc::vec::Vec; |
6 | |
7 | // ======================= |
8 | // Common helper functions |
9 | // ======================= |
10 | |
11 | /// Check if a character (well, u8) is an octal digit |
12 | fn is_octal_digit(c: u8) -> bool { |
13 | matches!(c, b'0' | b'1' | b'2' | b'3' | b'4' | b'5' | b'6' | b'7' ) |
14 | } |
15 | |
16 | /// Find the longest prefix of the given data where the given callback returns true |
17 | fn parse_with_matcher<M>(data: &[u8], matcher: M) -> (&[u8], &[u8]) |
18 | where |
19 | M: Fn(u8) -> bool, |
20 | { |
21 | let end: usize = data |
22 | .iter() |
23 | .enumerate() |
24 | .find(|(_, &c)| !matcher(c)) |
25 | .map(|(idx, _)| idx) |
26 | .unwrap_or(default:data.len()); |
27 | (&data[..end], &data[end..]) |
28 | } |
29 | |
30 | /// Check if a character is allowed in a quark name |
31 | fn allowed_in_quark_name(c: u8) -> bool { |
32 | c.is_ascii_alphanumeric() || c == b'-' || c == b'_' |
33 | } |
34 | |
35 | /// Find the longest prefix satisfying allowed_in_quark_name(). |
36 | /// This returns (Some(prefix), remaining) if a prefix is found, else (None, data). |
37 | fn next_component(data: &[u8]) -> (Option<&[u8]>, &[u8]) { |
38 | let (prefix: &[u8], remaining: &[u8]) = parse_with_matcher(data, matcher:allowed_in_quark_name); |
39 | match prefix { |
40 | [] => (None, remaining), |
41 | prefix: &[u8] => (Some(prefix), remaining), |
42 | } |
43 | } |
44 | |
45 | // ========================= |
46 | // Parser for resource files |
47 | // ========================= |
48 | |
49 | /// Skip to the next end of line in the given data |
50 | fn skip_to_eol(data: &[u8]) -> &[u8] { |
51 | parse_with_matcher(data, |c: u8| c != b' \n' ).1 |
52 | } |
53 | |
54 | /// Skip all spaces in the given data |
55 | fn skip_spaces(data: &[u8]) -> &[u8] { |
56 | parse_with_matcher(data, |c: u8| c == b' ' ).1 |
57 | } |
58 | |
59 | /// Skip the given text. Returns `None` if the text was not found |
60 | fn skip_text<'a>(data: &'a [u8], text: &[u8]) -> Option<&'a [u8]> { |
61 | if data.starts_with(needle:text) { |
62 | Some(&data[text.len()..]) |
63 | } else { |
64 | None |
65 | } |
66 | } |
67 | |
68 | /// Parse a single `Component` from the data. This can either be a wildcard ("?") or a |
69 | /// component made up of characters accepted by `allowed_in_quark_name`. |
70 | fn next_component_name(data: &[u8]) -> (Option<Component>, &[u8]) { |
71 | if data.first() == Some(&b'?' ) { |
72 | (Some(Component::Wildcard), &data[1..]) |
73 | } else { |
74 | let (comp: Option<&[u8]>, remaining: &[u8]) = next_component(data); |
75 | let comp: Option = comp.map(|s: &[u8]| { |
76 | let s: &str = std::str::from_utf8(s).expect(msg:"ascii-only" ); |
77 | Component::Normal(s.to_string()) |
78 | }); |
79 | (comp, remaining) |
80 | } |
81 | } |
82 | |
83 | /// Parse a resource like "foo.?*baz" (wildcards allowed) |
84 | fn parse_components(data: &[u8]) -> (Vec<(Binding, Component)>, &[u8]) { |
85 | fn parse_binding(mut data: &[u8]) -> (Binding, &[u8]) { |
86 | let mut binding = Binding::Tight; |
87 | loop { |
88 | match data.first() { |
89 | Some(&b'*' ) => binding = Binding::Loose, |
90 | Some(&b'.' ) => {} |
91 | _ => break, |
92 | } |
93 | data = &data[1..]; |
94 | } |
95 | (binding, data) |
96 | } |
97 | |
98 | let mut data = data; |
99 | let mut result = Vec::new(); |
100 | loop { |
101 | let (binding, remaining) = parse_binding(data); |
102 | if let (Some(component), remaining) = next_component_name(remaining) { |
103 | data = remaining; |
104 | result.push((binding, component)); |
105 | } else { |
106 | break; |
107 | } |
108 | } |
109 | (result, data) |
110 | } |
111 | |
112 | /// Parse a full entry from the data. This begins with components (see `parse_components()`), |
113 | /// then after a colon (":") comes the value. The value may contain escape sequences. |
114 | fn parse_entry(data: &[u8]) -> (Result<Entry, ()>, &[u8]) { |
115 | let (components, data) = parse_components(data); |
116 | |
117 | match components.last() { |
118 | // Empty components are not allowed |
119 | None => return (Err(()), skip_to_eol(data)), |
120 | // The last component may not be a wildcard |
121 | Some((_, Component::Wildcard)) => return (Err(()), skip_to_eol(data)), |
122 | _ => {} |
123 | } |
124 | |
125 | let data = skip_spaces(data); |
126 | |
127 | // next comes a colon |
128 | let data = match data.split_first() { |
129 | Some((&b':' , data)) => data, |
130 | _ => return (Err(()), skip_to_eol(data)), |
131 | }; |
132 | |
133 | // skip more spaces and let \ escape line breaks |
134 | let mut data = data; |
135 | loop { |
136 | let (_, remaining) = parse_with_matcher(data, |c| c == b' ' || c == b' \t' ); |
137 | if remaining.get(..2) == Some(&b" \\\n" [..]) { |
138 | data = &remaining[2..]; |
139 | } else { |
140 | data = remaining; |
141 | break; |
142 | } |
143 | } |
144 | |
145 | // Parse the value, decoding escape sequences. The most complicated case are octal escape |
146 | // sequences like \123. |
147 | let mut value = Vec::new(); |
148 | let mut index = 0; |
149 | let mut octal = None; |
150 | while let Some(&b) = data.get(index) { |
151 | index += 1; |
152 | if b == b' \n' { |
153 | break; |
154 | } |
155 | if let Some(oct) = octal { |
156 | if is_octal_digit(b) { |
157 | // We are currently parsing an octal; add the new character |
158 | match oct { |
159 | (x, None) => octal = Some((x, Some(b))), |
160 | (x, Some(y)) => { |
161 | let (x, y, z) = (x - b'0' , y - b'0' , b - b'0' ); |
162 | let decoded = (x * 8 + y) * 8 + z; |
163 | value.push(decoded); |
164 | octal = None; |
165 | } |
166 | } |
167 | continue; |
168 | } else { |
169 | // Not an octal sequence; add the collected characters to the output |
170 | value.push(b' \\' ); |
171 | value.push(oct.0); |
172 | if let Some(oct2) = oct.1 { |
173 | value.push(oct2); |
174 | } |
175 | octal = None; |
176 | |
177 | // Fall through to the parsing code below |
178 | } |
179 | } |
180 | if b != b' \\' { |
181 | value.push(b); |
182 | } else { |
183 | match data.get(index) { |
184 | None => { |
185 | value.push(b); |
186 | // Keep index as-is. This is to counter the += 1 below. |
187 | index -= 1; |
188 | } |
189 | Some(b' ' ) => value.push(b' ' ), |
190 | Some(b' \t' ) => value.push(b' \t' ), |
191 | Some(b'n' ) => value.push(b' \n' ), |
192 | Some(b' \\' ) => value.push(b' \\' ), |
193 | Some(b' \n' ) => { /* Continue parsing next line */ } |
194 | Some(&x) if is_octal_digit(x) => octal = Some((x, None)), |
195 | Some(&x) => { |
196 | value.push(b); |
197 | value.push(x); |
198 | } |
199 | } |
200 | index += 1; |
201 | } |
202 | } |
203 | |
204 | let entry = Entry { components, value }; |
205 | (Ok(entry), &data[index..]) |
206 | } |
207 | |
208 | /// Parse the contents of a database |
209 | pub(crate) fn parse_database<F>(mut data: &[u8], result: &mut Vec<Entry>, mut include_callback: F) |
210 | where |
211 | for<'r> F: FnMut(&'r [u8], &mut Vec<Entry>), |
212 | { |
213 | // Iterate over lines |
214 | while let Some(first) = data.first() { |
215 | match first { |
216 | // Skip empty lines |
217 | b' \n' => data = &data[1..], |
218 | // Comment, skip the line |
219 | b'!' => data = skip_to_eol(data), |
220 | b'#' => { |
221 | let remaining = skip_spaces(&data[1..]); |
222 | // Skip to the next line for the next loop iteration. The rest of the code here |
223 | // tried to parse the line. |
224 | data = skip_to_eol(remaining); |
225 | |
226 | // Only #include is defined |
227 | if let Some(remaining) = skip_text(remaining, b"include" ) { |
228 | let (_, remaining) = parse_with_matcher(remaining, |c| c == b' ' ); |
229 | // Find the text enclosed in quotation marks |
230 | if let Some(b' \"' ) = remaining.first() { |
231 | let (file, remaining) = |
232 | parse_with_matcher(&remaining[1..], |c| c != b'"' && c != b' \n' ); |
233 | if let Some(b' \"' ) = remaining.first() { |
234 | // Okay, we found a well-formed include directive. |
235 | include_callback(file, result); |
236 | } |
237 | } |
238 | } |
239 | } |
240 | _ => { |
241 | let (entry, remaining) = parse_entry(data); |
242 | data = remaining; |
243 | // Add the entry to the result if we parsed one; ignore errors |
244 | result.extend(entry.ok()); |
245 | } |
246 | } |
247 | } |
248 | } |
249 | |
250 | /// Parse a resource query like "foo.bar.baz" (no wildcards allowed, no bindings allowed) |
251 | pub(crate) fn parse_query(data: &[u8]) -> Option<Vec<String>> { |
252 | let mut data: &[u8] = data; |
253 | let mut result: Vec = Vec::new(); |
254 | while let (Some(component: &[u8]), remaining: &[u8]) = next_component(data) { |
255 | data = remaining; |
256 | while let Some(&b'.' ) = data.first() { |
257 | data = &data[1..]; |
258 | } |
259 | let component: &str = std::str::from_utf8(component).expect(msg:"ascii-only" ); |
260 | result.push(component.to_string()); |
261 | } |
262 | if data.is_empty() { |
263 | Some(result) |
264 | } else { |
265 | None |
266 | } |
267 | } |
268 | |
269 | #[cfg (test)] |
270 | mod test { |
271 | use super::{parse_database, parse_entry, parse_query, Binding, Component, Entry}; |
272 | use alloc::string::{String, ToString}; |
273 | use alloc::vec; |
274 | use alloc::vec::Vec; |
275 | use std::eprintln; |
276 | |
277 | // Most tests in here are based on [1], which is: Copyright © 2016 Ingo Bürk |
278 | // [1]: https://github.com/Airblader/xcb-util-xrm/blob/master/tests/tests_parser.c |
279 | |
280 | #[test ] |
281 | fn test_parse_query_success() { |
282 | let tests = [ |
283 | ( |
284 | &b"First.second" [..], |
285 | vec!["First" .to_string(), "second" .to_string()], |
286 | ), |
287 | (b"" , Vec::new()), |
288 | ( |
289 | b"urxvt.scrollBar_right" , |
290 | vec!["urxvt" .to_string(), "scrollBar_right" .to_string()], |
291 | ), |
292 | ( |
293 | b"urxvt.Control-Shift-Up" , |
294 | vec!["urxvt" .to_string(), "Control-Shift-Up" .to_string()], |
295 | ), |
296 | ]; |
297 | for (data, expected) in tests.iter() { |
298 | let result = parse_query(data); |
299 | assert_eq!(result.as_ref(), Some(expected), "while parsing {:?}" , data); |
300 | } |
301 | } |
302 | |
303 | #[test ] |
304 | fn test_parse_query_error() { |
305 | let tests = [ |
306 | &b"First.second: on" [..], |
307 | b"First*second" , |
308 | b"First.?.second" , |
309 | b"*second" , |
310 | b"?.second" , |
311 | ]; |
312 | for data in tests.iter() { |
313 | let result = parse_query(data); |
314 | assert!( |
315 | result.is_none(), |
316 | "Unexpected success parsing ' {:?}': {:?}" , |
317 | data, |
318 | result, |
319 | ); |
320 | } |
321 | } |
322 | |
323 | #[test ] |
324 | fn test_parse_entry_success() { |
325 | let tests = [ |
326 | // Basics |
327 | ( |
328 | &b"First: 1" [..], |
329 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
330 | &b"1" [..], |
331 | ), |
332 | ( |
333 | b"First.second: 1" , |
334 | vec![ |
335 | (Binding::Tight, Component::Normal("First" .to_string())), |
336 | (Binding::Tight, Component::Normal("second" .to_string())), |
337 | ], |
338 | b"1" , |
339 | ), |
340 | ( |
341 | b"First..second: 1" , |
342 | vec![ |
343 | (Binding::Tight, Component::Normal("First" .to_string())), |
344 | (Binding::Tight, Component::Normal("second" .to_string())), |
345 | ], |
346 | b"1" , |
347 | ), |
348 | // Wildcards |
349 | ( |
350 | b"?.second: 1" , |
351 | vec![ |
352 | (Binding::Tight, Component::Wildcard), |
353 | (Binding::Tight, Component::Normal("second" .to_string())), |
354 | ], |
355 | b"1" , |
356 | ), |
357 | ( |
358 | b"First.?.third: 1" , |
359 | vec![ |
360 | (Binding::Tight, Component::Normal("First" .to_string())), |
361 | (Binding::Tight, Component::Wildcard), |
362 | (Binding::Tight, Component::Normal("third" .to_string())), |
363 | ], |
364 | b"1" , |
365 | ), |
366 | // Loose bindings |
367 | ( |
368 | b"*second: 1" , |
369 | vec![(Binding::Loose, Component::Normal("second" .to_string()))], |
370 | b"1" , |
371 | ), |
372 | ( |
373 | b"First*third: 1" , |
374 | vec![ |
375 | (Binding::Tight, Component::Normal("First" .to_string())), |
376 | (Binding::Loose, Component::Normal("third" .to_string())), |
377 | ], |
378 | b"1" , |
379 | ), |
380 | ( |
381 | b"First**third: 1" , |
382 | vec![ |
383 | (Binding::Tight, Component::Normal("First" .to_string())), |
384 | (Binding::Loose, Component::Normal("third" .to_string())), |
385 | ], |
386 | b"1" , |
387 | ), |
388 | // Combinations |
389 | ( |
390 | b"First*?.fourth: 1" , |
391 | vec![ |
392 | (Binding::Tight, Component::Normal("First" .to_string())), |
393 | (Binding::Loose, Component::Wildcard), |
394 | (Binding::Tight, Component::Normal("fourth" .to_string())), |
395 | ], |
396 | b"1" , |
397 | ), |
398 | // Values |
399 | ( |
400 | b"First: 1337" , |
401 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
402 | b"1337" , |
403 | ), |
404 | ( |
405 | b"First: -1337" , |
406 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
407 | b"-1337" , |
408 | ), |
409 | ( |
410 | b"First: 13.37" , |
411 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
412 | b"13.37" , |
413 | ), |
414 | ( |
415 | b"First: value" , |
416 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
417 | b"value" , |
418 | ), |
419 | ( |
420 | b"First: #abcdef" , |
421 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
422 | b"#abcdef" , |
423 | ), |
424 | ( |
425 | b"First: { key: 'value' }" , |
426 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
427 | b"{ key: 'value' }" , |
428 | ), |
429 | ( |
430 | b"First: x?y" , |
431 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
432 | b"x?y" , |
433 | ), |
434 | ( |
435 | b"First: x*y" , |
436 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
437 | b"x*y" , |
438 | ), |
439 | // Whitespace |
440 | ( |
441 | b"First: x" , |
442 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
443 | b"x" , |
444 | ), |
445 | ( |
446 | b"First: x " , |
447 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
448 | b"x " , |
449 | ), |
450 | ( |
451 | b"First: x " , |
452 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
453 | b"x " , |
454 | ), |
455 | ( |
456 | b"First:x" , |
457 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
458 | b"x" , |
459 | ), |
460 | ( |
461 | b"First: \t x" , |
462 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
463 | b"x" , |
464 | ), |
465 | ( |
466 | b"First: \t x \t" , |
467 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
468 | b"x \t" , |
469 | ), |
470 | // Special characters |
471 | ( |
472 | b"First: \\ x" , |
473 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
474 | b" x" , |
475 | ), |
476 | ( |
477 | b"First: x \\ x" , |
478 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
479 | b"x x" , |
480 | ), |
481 | ( |
482 | b"First: \\\tx" , |
483 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
484 | b" \tx" , |
485 | ), |
486 | ( |
487 | b"First: \\011x" , |
488 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
489 | b" \tx" , |
490 | ), |
491 | ( |
492 | b"First: x \\\\x" , |
493 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
494 | b"x \\x" , |
495 | ), |
496 | ( |
497 | b"First: x \\nx" , |
498 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
499 | b"x \nx" , |
500 | ), |
501 | ( |
502 | b"First: \\080" , |
503 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
504 | b" \\080" , |
505 | ), |
506 | ( |
507 | b"First: \\00a" , |
508 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
509 | b" \\00a" , |
510 | ), |
511 | // Own tests |
512 | // Some more escape tests, e.g. escape at end of input |
513 | ( |
514 | b"First: \\" , |
515 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
516 | b" \\" , |
517 | ), |
518 | ( |
519 | b"First: \\xxx" , |
520 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
521 | b" \\xxx" , |
522 | ), |
523 | ( |
524 | b"First: \\1xx" , |
525 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
526 | b" \\1xx" , |
527 | ), |
528 | ( |
529 | b"First: \\10x" , |
530 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
531 | b" \\10x" , |
532 | ), |
533 | ( |
534 | b"First: \\100" , |
535 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
536 | b"@" , |
537 | ), |
538 | ( |
539 | b"First: \\n" , |
540 | vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
541 | b" \n" , |
542 | ), |
543 | ]; |
544 | for (data, resource, value) in tests.iter() { |
545 | run_entry_test(data, resource, value); |
546 | } |
547 | } |
548 | |
549 | #[test ] |
550 | fn test_parse_entry_error() { |
551 | let tests = [ |
552 | &b": 1" [..], |
553 | b"?: 1" , |
554 | b"First" , |
555 | b"First second" , |
556 | b"First.?: 1" , |
557 | b"F \xc3\xb6rst: 1" , |
558 | b"F~rst: 1" , |
559 | ]; |
560 | for data in tests.iter() { |
561 | match parse_entry(data) { |
562 | (Ok(v), _) => panic!("Unexpected success parsing ' {:?}': {:?}" , data, v), |
563 | (Err(_), b"" ) => {} |
564 | (Err(_), remaining) => panic!( |
565 | "Unexpected remaining data parsing ' {:?}': {:?}" , |
566 | data, remaining |
567 | ), |
568 | } |
569 | } |
570 | } |
571 | |
572 | #[test ] |
573 | fn test_parse_large_value() { |
574 | let value = vec![b'x' ; 1025]; |
575 | let mut data = b"First: " .to_vec(); |
576 | data.extend(&value); |
577 | let resource = (Binding::Tight, Component::Normal("First" .to_string())); |
578 | run_entry_test(&data, &[resource], &value); |
579 | } |
580 | |
581 | #[test ] |
582 | fn test_parse_large_resource() { |
583 | let x = vec![b'x' ; 1025]; |
584 | let y = vec![b'y' ; 1025]; |
585 | let mut data = x.clone(); |
586 | data.push(b'.' ); |
587 | data.extend(&y); |
588 | data.extend(b": 1" ); |
589 | let resource = [ |
590 | ( |
591 | Binding::Tight, |
592 | Component::Normal(String::from_utf8(x).unwrap()), |
593 | ), |
594 | ( |
595 | Binding::Tight, |
596 | Component::Normal(String::from_utf8(y).unwrap()), |
597 | ), |
598 | ]; |
599 | run_entry_test(&data, &resource, b"1" ); |
600 | } |
601 | |
602 | #[test ] |
603 | fn test_parse_database() { |
604 | let expected_entry = Entry { |
605 | components: vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
606 | value: b"1" .to_vec(), |
607 | }; |
608 | let tests = [ |
609 | (&b"First: 1 \n\n\n" [..], vec![expected_entry.clone()]), |
610 | (b"First: 1 \n!Foo" , vec![expected_entry.clone()]), |
611 | (b"!First: 1 \nbar \n\n\n" , Vec::new()), |
612 | (b"!bar \nFirst: 1 \nbaz" , vec![expected_entry.clone()]), |
613 | (b"First : \\\n \\\n\\\n1 \n" , vec![expected_entry]), |
614 | ( |
615 | b"First: \\\n 1 \\\n2 \n" , |
616 | vec![Entry { |
617 | components: vec![(Binding::Tight, Component::Normal("First" .to_string()))], |
618 | value: b"12" .to_vec(), |
619 | }], |
620 | ), |
621 | ]; |
622 | let mut success = true; |
623 | for (data, expected) in tests.iter() { |
624 | let mut result = Vec::new(); |
625 | parse_database(data, &mut result, |_, _| unreachable!()); |
626 | if &result != expected { |
627 | eprintln!("While testing {:?}" , data); |
628 | eprintln!("Expected: {:?}" , expected); |
629 | eprintln!("Got: {:?}" , result); |
630 | eprintln!(); |
631 | success = false; |
632 | } |
633 | } |
634 | if !success { |
635 | panic!() |
636 | } |
637 | } |
638 | |
639 | #[test ] |
640 | fn test_include_parsing() { |
641 | let tests = [ |
642 | (&b"#include \"test \"" [..], vec![&b"test" [..]]), |
643 | (b"#include \"test" , Vec::new()), |
644 | (b"#include \"" , Vec::new()), |
645 | (b"#include" , Vec::new()), |
646 | (b"#includ" , Vec::new()), |
647 | (b"#in" , Vec::new()), |
648 | (b"# foo" , Vec::new()), |
649 | ( |
650 | b"# include \" test \" \n#include \"foo \"" , |
651 | vec![b" test " , b"foo" ], |
652 | ), |
653 | ]; |
654 | let mut success = true; |
655 | for (data, expected) in tests.iter() { |
656 | let mut result = Vec::new(); |
657 | let mut calls = Vec::new(); |
658 | parse_database(data, &mut result, |file, _| calls.push(file.to_vec())); |
659 | if &calls != expected { |
660 | eprintln!("While testing {:?}" , data); |
661 | eprintln!("Expected: {:?}" , expected); |
662 | eprintln!("Got: {:?}" , calls); |
663 | eprintln!(); |
664 | success = false; |
665 | } |
666 | } |
667 | if !success { |
668 | panic!() |
669 | } |
670 | } |
671 | |
672 | #[test ] |
673 | fn test_include_additions() { |
674 | let entry = Entry { |
675 | components: Vec::new(), |
676 | value: b"42" .to_vec(), |
677 | }; |
678 | let mut result = Vec::new(); |
679 | parse_database(b"#include \"test \"" , &mut result, |file, result| { |
680 | assert_eq!(file, b"test" ); |
681 | result.push(entry.clone()); |
682 | }); |
683 | assert_eq!(result, [entry]); |
684 | } |
685 | |
686 | fn run_entry_test(data: &[u8], resource: &[(Binding, Component)], value: &[u8]) { |
687 | match parse_entry(data) { |
688 | (Ok(result), remaining) => { |
689 | assert_eq!(remaining, b"" , "failed to parse {:?}" , data); |
690 | assert_eq!( |
691 | result.components, resource, |
692 | "incorrect components when parsing {:?}" , |
693 | data |
694 | ); |
695 | assert_eq!( |
696 | result.value, value, |
697 | "incorrect value when parsing {:?}" , |
698 | data |
699 | ); |
700 | } |
701 | (Err(err), _) => panic!("Failed to parse ' {:?}': {:?}" , data, err), |
702 | } |
703 | } |
704 | } |
705 | |