1use std::slice::Iter;
2
3use crate::{HasSpan, Parser, Span, document::Author};
4
5#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct AuthorLine<'src> {
10 authors: Vec<Author>,
11 source: Span<'src>,
12}
13
14impl<'src> AuthorLine<'src> {
15 pub(crate) fn parse(source: Span<'src>, parser: &mut Parser) -> Self {
16 let authors: Vec<Author> = source
17 .data()
18 .split("; ")
19 .filter_map(|raw_author| Author::parse(raw_author, parser))
20 .collect();
21
22 for (index, author) in authors.iter().enumerate() {
23 set_nth_attribute(parser, "author", index, author.name());
24 set_nth_attribute(parser, "authorinitials", index, author.initials());
25 set_nth_attribute(parser, "firstname", index, author.firstname());
26
27 if let Some(middlename) = author.middlename() {
28 set_nth_attribute(parser, "middlename", index, middlename);
29 }
30
31 if let Some(lastname) = author.lastname() {
32 set_nth_attribute(parser, "lastname", index, lastname);
33 }
34
35 if let Some(email) = author.email() {
36 set_nth_attribute(parser, "email", index, email);
37 }
38 }
39
40 Self { authors, source }
41 }
42
43 pub fn authors(&'src self) -> Iter<'src, Author> {
45 self.authors.iter()
46 }
47}
48
49fn set_nth_attribute<V: AsRef<str>>(parser: &mut Parser, name: &str, index: usize, value: V) {
50 let name = if index == 0 {
51 name.to_string()
52 } else {
53 format!("{name}_{count}", count = index + 1)
54 };
55
56 parser.set_attribute_by_value_from_header(name, value);
57}
58
59impl<'src> HasSpan<'src> for AuthorLine<'src> {
60 fn span(&self) -> Span<'src> {
61 self.source
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use pretty_assertions_sorted::assert_eq;
68
69 use crate::{Parser, parser::ModificationContext, tests::prelude::*};
70
71 #[test]
72 fn empty_line() {
73 let mut parser = Parser::default();
74
75 let al = crate::document::AuthorLine::parse(crate::Span::new(""), &mut parser);
76
77 assert_eq!(
78 &al,
79 AuthorLine {
80 authors: &[],
81 source: Span {
82 data: "",
83 line: 1,
84 col: 1,
85 offset: 0,
86 },
87 }
88 );
89 }
90
91 #[test]
92 fn attr_sub_with_html_encoding_fallback() {
93 let mut parser = Parser::default().with_intrinsic_attribute(
97 "weird-content",
98 "Complex <weird> & stuff",
99 ModificationContext::Anywhere,
100 );
101
102 let al = crate::document::AuthorLine::parse(
103 crate::Span::new("Some {weird-content} pattern"),
104 &mut parser,
105 );
106
107 assert_eq!(
108 al,
109 AuthorLine {
110 authors: &[Author {
111 name: "Some Complex <weird> & stuff pattern",
112 firstname: "Some Complex <weird> & stuff pattern",
113 middlename: None,
114 lastname: None,
115 email: None,
116 },],
117 source: Span {
118 data: "Some {weird-content} pattern",
119 line: 1,
120 col: 1,
121 offset: 0,
122 },
123 }
124 );
125 }
126
127 #[test]
128 fn empty_author() {
129 let mut parser = Parser::default();
130
131 let al = crate::document::AuthorLine::parse(
132 crate::Span::new("Author One; ; Author Three"),
133 &mut parser,
134 );
135
136 assert_eq!(
137 al,
138 AuthorLine {
139 authors: &[
140 Author {
141 name: "Author One",
142 firstname: "Author",
143 middlename: None,
144 lastname: Some("One",),
145 email: None,
146 },
147 Author {
148 name: "Author Three",
149 firstname: "Author",
150 middlename: None,
151 lastname: Some("Three",),
152 email: None,
153 },
154 ],
155 source: Span {
156 data: "Author One; ; Author Three",
157 line: 1,
158 col: 1,
159 offset: 0,
160 },
161 }
162 );
163 }
164
165 #[test]
166 fn one_simple_author() {
167 let mut parser = Parser::default();
168
169 let al = crate::document::AuthorLine::parse(
170 crate::Span::new("Kismet R. Lee <kismet@asciidoctor.org>"),
171 &mut parser,
172 );
173
174 assert_eq!(
175 &al,
176 AuthorLine {
177 authors: &[Author {
178 name: "Kismet R. Lee",
179 firstname: "Kismet",
180 middlename: Some("R.",),
181 lastname: Some("Lee",),
182 email: Some("kismet@asciidoctor.org",),
183 },],
184 source: Span {
185 data: "Kismet R. Lee <kismet@asciidoctor.org>",
186 line: 1,
187 col: 1,
188 offset: 0,
189 },
190 }
191 );
192 }
193
194 #[test]
195 fn author_without_middle_name() {
196 let mut parser = Parser::default();
197
198 let al = crate::document::AuthorLine::parse(
199 crate::Span::new("Doc Writer <doc@example.com>"),
200 &mut parser,
201 );
202
203 assert_eq!(
204 al,
205 AuthorLine {
206 authors: &[Author {
207 name: "Doc Writer",
208 firstname: "Doc",
209 middlename: None,
210 lastname: Some("Writer",),
211 email: Some("doc@example.com",),
212 },],
213 source: Span {
214 data: "Doc Writer <doc@example.com>",
215 line: 1,
216 col: 1,
217 offset: 0,
218 },
219 }
220 );
221 }
222
223 #[test]
224 fn too_many_names() {
225 let mut parser = Parser::default();
226
227 let al = crate::document::AuthorLine::parse(
228 crate::Span::new("Four Names Not Supported <doc@example.com>"),
229 &mut parser,
230 );
231
232 assert_eq!(
233 al,
234 AuthorLine {
235 authors: &[Author {
236 name: "Four Names Not Supported <doc@example.com>",
237 firstname: "Four Names Not Supported <doc@example.com>",
238 middlename: None,
239 lastname: None,
240 email: None,
241 },],
242 source: Span {
243 data: "Four Names Not Supported <doc@example.com>",
244 line: 1,
245 col: 1,
246 offset: 0,
247 },
248 }
249 );
250 }
251
252 #[test]
253 fn one_name() {
254 let mut parser = Parser::default();
255
256 let al = crate::document::AuthorLine::parse(
257 crate::Span::new("John <john@example.com>"),
258 &mut parser,
259 );
260
261 assert_eq!(
262 al,
263 AuthorLine {
264 authors: &[Author {
265 name: "John",
266 firstname: "John",
267 middlename: None,
268 lastname: None,
269 email: Some("john@example.com",),
270 },],
271 source: Span {
272 data: "John <john@example.com>",
273 line: 1,
274 col: 1,
275 offset: 0,
276 },
277 }
278 );
279 }
280
281 #[test]
282 fn underscore_join() {
283 let mut parser = Parser::default();
284
285 let al =
286 crate::document::AuthorLine::parse(crate::Span::new("Mary_Sue Brontë"), &mut parser);
287
288 assert_eq!(
289 al,
290 AuthorLine {
291 authors: &[Author {
292 name: "Mary Sue Brontë", firstname: "Mary Sue", middlename: None,
295 lastname: Some("Brontë",),
296 email: None,
297 },],
298 source: Span {
299 data: "Mary_Sue Brontë",
300 line: 1,
301 col: 1,
302 offset: 0,
303 },
304 }
305 );
306 }
307
308 #[test]
309 fn greek() {
310 let mut parser = Parser::default();
311
312 let al = crate::document::AuthorLine::parse(
313 crate::Span::new("Αλέξανδρος Παπαδόπουλος"),
314 &mut parser,
315 );
316
317 assert_eq!(
318 al,
319 AuthorLine {
320 authors: &[Author {
321 name: "Αλέξανδρος Παπαδόπουλος",
322 firstname: "Αλέξανδρος",
323 middlename: None,
324 lastname: Some("Παπαδόπουλος",),
325 email: None,
326 },],
327 source: Span {
328 data: "Αλέξανδρος Παπαδόπουλος",
329 line: 1,
330 col: 1,
331 offset: 0,
332 },
333 }
334 );
335 }
336
337 #[test]
338 fn japanese() {
339 let mut parser = Parser::default();
340
341 let al = crate::document::AuthorLine::parse(crate::Span::new("山田太郎"), &mut parser);
342
343 assert_eq!(
344 al,
345 AuthorLine {
346 authors: &[Author {
347 name: "山田太郎",
348 firstname: "山田太郎",
349 middlename: None,
350 lastname: None,
351 email: None,
352 },],
353 source: Span {
354 data: "山田太郎",
355 line: 1,
356 col: 1,
357 offset: 0,
358 },
359 }
360 );
361 }
362
363 #[test]
364 fn arabic() {
365 let mut parser = Parser::default();
366
367 let al = crate::document::AuthorLine::parse(crate::Span::new("عبد_الله"), &mut parser);
368
369 assert_eq!(
370 al,
371 AuthorLine {
372 authors: &[Author {
373 name: "عبد الله", firstname: "عبد الله", middlename: None,
376 lastname: None,
377 email: None,
378 },],
379 source: Span {
380 data: "عبد_الله",
381 line: 1,
382 col: 1,
383 offset: 0,
384 },
385 }
386 );
387 }
388
389 #[test]
390 fn underscore_replacement_in_all_name_parts() {
391 let mut parser = Parser::default();
392
393 let al = crate::document::AuthorLine::parse(
394 crate::Span::new("John_Paul Mary_Jane Smith_Jones <email@example.com>"),
395 &mut parser,
396 );
397
398 assert_eq!(
399 al,
400 AuthorLine {
401 authors: &[Author {
402 name: "John Paul Mary Jane Smith Jones", firstname: "John Paul", middlename: Some("Mary Jane"), lastname: Some("Smith Jones"), email: Some("email@example.com"),
407 },],
408 source: Span {
409 data: "John_Paul Mary_Jane Smith_Jones <email@example.com>",
410 line: 1,
411 col: 1,
412 offset: 0,
413 },
414 }
415 );
416 }
417
418 #[test]
419 fn multiple_underscores_in_name_parts() {
420 let mut parser = Parser::default();
421
422 let al =
423 crate::document::AuthorLine::parse(crate::Span::new("A_B_C D_E_F G_H_I"), &mut parser);
424
425 assert_eq!(
426 al,
427 AuthorLine {
428 authors: &[Author {
429 name: "A B C D E F G H I", firstname: "A B C", middlename: Some("D E F"), lastname: Some("G H I"), email: None,
434 },],
435 source: Span {
436 data: "A_B_C D_E_F G_H_I",
437 line: 1,
438 col: 1,
439 offset: 0,
440 },
441 }
442 );
443 }
444
445 #[test]
446 fn underscore_replacement_with_attribute_substitution() {
447 let mut parser = Parser::default()
448 .with_intrinsic_attribute("first-part", "John_Paul", ModificationContext::Anywhere)
449 .with_intrinsic_attribute("last-part", "Smith_Jones", ModificationContext::Anywhere);
450
451 let al = crate::document::AuthorLine::parse(
452 crate::Span::new("{first-part} {last-part} <email@example.com>"),
453 &mut parser,
454 );
455
456 assert_eq!(
461 al,
462 AuthorLine {
463 authors: &[Author {
464 name: "John Paul Smith Jones <email@example.com>", firstname: "John Paul Smith Jones <email@example.com>", middlename: None,
471 lastname: None,
472 email: None,
473 },],
474 source: Span {
475 data: "{first-part} {last-part} <email@example.com>",
476 line: 1,
477 col: 1,
478 offset: 0,
479 },
480 }
481 );
482 }
483
484 #[test]
485 fn attr_sub_email() {
486 let mut parser = Parser::default()
487 .with_intrinsic_attribute(
488 "jane-email",
489 "jane@example.com",
490 ModificationContext::Anywhere,
491 )
492 .with_intrinsic_attribute(
493 "john-email",
494 "john@example.com",
495 ModificationContext::Anywhere,
496 );
497
498 let al = crate::document::AuthorLine::parse(
499 crate::Span::new("Jane Smith <{jane-email}>; John Doe <{john-email}>"),
500 &mut parser,
501 );
502
503 assert_eq!(
504 al,
505 AuthorLine {
506 authors: &[
507 Author {
508 name: "Jane Smith",
509 firstname: "Jane",
510 middlename: None,
511 lastname: Some("Smith",),
512 email: Some("jane@example.com",),
513 },
514 Author {
515 name: "John Doe",
516 firstname: "John",
517 middlename: None,
518 lastname: Some("Doe",),
519 email: Some("john@example.com",),
520 },
521 ],
522 source: Span {
523 data: "Jane Smith <{jane-email}>; John Doe <{john-email}>",
524 line: 1,
525 col: 1,
526 offset: 0,
527 },
528 }
529 );
530 }
531
532 #[test]
533 fn attr_sub_applied_after_parsing() {
534 let mut parser = Parser::default().with_intrinsic_attribute(
539 "author-list",
540 "Jane Smith <jane@example.com>; John Doe <john@example.com>",
541 ModificationContext::Anywhere,
542 );
543
544 let al = crate::document::AuthorLine::parse(crate::Span::new("{author-list}"), &mut parser);
545
546 assert_eq!(
547 al,
548 AuthorLine {
549 authors: &[Author {
550 name: "Jane Smith <jane@example.com>; John Doe <john@example.com>",
551 firstname: "Jane Smith <jane@example.com>; John Doe <john@example.com>",
552 middlename: None,
553 lastname: None,
554 email: None,
555 },],
556 source: Span {
557 data: "{author-list}",
558 line: 1,
559 col: 1,
560 offset: 0,
561 },
562 }
563 );
564 }
565
566 #[test]
567 fn attr_sub_for_individual_author() {
568 let mut parser = Parser::default().with_intrinsic_attribute(
569 "full-author",
570 "John Doe <john@example.com>",
571 ModificationContext::Anywhere,
572 );
573
574 let al = crate::document::AuthorLine::parse(crate::Span::new("{full-author}"), &mut parser);
575
576 assert_eq!(
577 al,
578 AuthorLine {
579 authors: &[Author {
580 name: "John Doe <john@example.com>",
581 firstname: "John Doe <john@example.com>",
582 middlename: None,
583 lastname: None,
584 email: None,
585 },],
586 source: Span {
587 data: "{full-author}",
588 line: 1,
589 col: 1,
590 offset: 0,
591 },
592 }
593 );
594 }
595
596 #[test]
597 fn err_individual_name_components_as_attributes() {
598 let mut parser = Parser::default()
600 .with_intrinsic_attribute("first-name", "Jane", ModificationContext::Anywhere)
601 .with_intrinsic_attribute("last-name", "Smith", ModificationContext::Anywhere)
602 .with_intrinsic_attribute(
603 "author-email",
604 "jane@example.com",
605 ModificationContext::Anywhere,
606 );
607
608 let al = crate::document::AuthorLine::parse(
609 crate::Span::new("{first-name} {last-name} <{author-email}>"),
610 &mut parser,
611 );
612
613 assert_eq!(
614 al,
615 AuthorLine {
616 authors: &[Author {
617 name: "Jane Smith <jane@example.com>",
618 firstname: "Jane Smith <jane@example.com>",
619 middlename: None,
620 lastname: None,
621 email: None,
622 },],
623 source: Span {
624 data: "{first-name} {last-name} <{author-email}>",
625 line: 1,
626 col: 1,
627 offset: 0,
628 },
629 }
630 );
631 }
632
633 #[test]
634 fn sets_author_attributes_single_author_with_all_parts() {
635 let mut parser = Parser::default();
636 let _doc = parser.parse("= Document Title\nKismet R. Lee <kismet@asciidoctor.org>");
637
638 assert_eq!(
640 parser.attribute_value("author"),
641 InterpretedValue::Value("Kismet R. Lee")
642 );
643 assert_eq!(
644 parser.attribute_value("firstname"),
645 InterpretedValue::Value("Kismet")
646 );
647 assert_eq!(
648 parser.attribute_value("middlename"),
649 InterpretedValue::Value("R.")
650 );
651 assert_eq!(
652 parser.attribute_value("lastname"),
653 InterpretedValue::Value("Lee")
654 );
655 assert_eq!(
656 parser.attribute_value("authorinitials"),
657 InterpretedValue::Value("KRL")
658 );
659 assert_eq!(
660 parser.attribute_value("email"),
661 InterpretedValue::Value("kismet@asciidoctor.org")
662 );
663 }
664
665 #[test]
666 fn sets_author_attributes_single_author_without_middle_name() {
667 let mut parser = Parser::default();
668 let _doc = parser.parse("= Document Title\nDoc Writer <doc@example.com>");
669
670 assert_eq!(
671 parser.attribute_value("author"),
672 InterpretedValue::Value("Doc Writer")
673 );
674 assert_eq!(
675 parser.attribute_value("firstname"),
676 InterpretedValue::Value("Doc")
677 );
678 assert_eq!(
679 parser.attribute_value("middlename"),
680 InterpretedValue::Unset
681 );
682 assert_eq!(
683 parser.attribute_value("lastname"),
684 InterpretedValue::Value("Writer")
685 );
686 assert_eq!(
687 parser.attribute_value("authorinitials"),
688 InterpretedValue::Value("DW")
689 );
690 assert_eq!(
691 parser.attribute_value("email"),
692 InterpretedValue::Value("doc@example.com")
693 );
694 }
695
696 #[test]
697 fn sets_author_attributes_single_author_first_name_only() {
698 let mut parser = Parser::default();
699 let _doc = parser.parse("= Document Title\nJohn <john@example.com>");
700
701 assert_eq!(
702 parser.attribute_value("author"),
703 InterpretedValue::Value("John")
704 );
705 assert_eq!(
706 parser.attribute_value("firstname"),
707 InterpretedValue::Value("John")
708 );
709 assert_eq!(
710 parser.attribute_value("middlename"),
711 InterpretedValue::Unset
712 );
713 assert_eq!(parser.attribute_value("lastname"), InterpretedValue::Unset);
714 assert_eq!(
715 parser.attribute_value("authorinitials"),
716 InterpretedValue::Value("J")
717 );
718 assert_eq!(
719 parser.attribute_value("email"),
720 InterpretedValue::Value("john@example.com")
721 );
722 }
723
724 #[test]
725 fn sets_author_attributes_single_author_without_email() {
726 let mut parser = Parser::default();
727 let _doc = parser.parse("= Document Title\nMary Sue Brontë");
728
729 assert_eq!(
730 parser.attribute_value("author"),
731 InterpretedValue::Value("Mary Sue Brontë")
732 );
733 assert_eq!(
734 parser.attribute_value("firstname"),
735 InterpretedValue::Value("Mary")
736 );
737 assert_eq!(
738 parser.attribute_value("middlename"),
739 InterpretedValue::Value("Sue")
740 );
741 assert_eq!(
742 parser.attribute_value("lastname"),
743 InterpretedValue::Value("Brontë")
744 );
745 assert_eq!(
746 parser.attribute_value("authorinitials"),
747 InterpretedValue::Value("MSB")
748 );
749 assert_eq!(parser.attribute_value("email"), InterpretedValue::Unset);
750 }
751
752 #[test]
753 fn sets_author_attributes_multiple_authors() {
754 let mut parser = Parser::default();
755 let _doc = parser
756 .parse("= Document Title\nJane Smith <jane@example.com>; John Doe <john@example.com>");
757
758 assert_eq!(
760 parser.attribute_value("author"),
761 InterpretedValue::Value("Jane Smith")
762 );
763 assert_eq!(
764 parser.attribute_value("firstname"),
765 InterpretedValue::Value("Jane")
766 );
767 assert_eq!(
768 parser.attribute_value("lastname"),
769 InterpretedValue::Value("Smith")
770 );
771 assert_eq!(
772 parser.attribute_value("authorinitials"),
773 InterpretedValue::Value("JS")
774 );
775 assert_eq!(
776 parser.attribute_value("email"),
777 InterpretedValue::Value("jane@example.com")
778 );
779
780 assert_eq!(
782 parser.attribute_value("author_2"),
783 InterpretedValue::Value("John Doe")
784 );
785 assert_eq!(
786 parser.attribute_value("firstname_2"),
787 InterpretedValue::Value("John")
788 );
789 assert_eq!(
790 parser.attribute_value("lastname_2"),
791 InterpretedValue::Value("Doe")
792 );
793 assert_eq!(
794 parser.attribute_value("authorinitials_2"),
795 InterpretedValue::Value("JD")
796 );
797 assert_eq!(
798 parser.attribute_value("email_2"),
799 InterpretedValue::Value("john@example.com")
800 );
801
802 assert_eq!(
804 parser.attribute_value("middlename"),
805 InterpretedValue::Unset
806 );
807 assert_eq!(
808 parser.attribute_value("middlename_2"),
809 InterpretedValue::Unset
810 );
811 }
812
813 #[test]
814 fn sets_author_attributes_unicode_names() {
815 let mut parser = Parser::default();
816 let _doc = parser.parse("= Document Title\nΑλέξανδρος Μ. Παπαδόπουλος");
817
818 assert_eq!(
819 parser.attribute_value("author"),
820 InterpretedValue::Value("Αλέξανδρος Μ. Παπαδόπουλος")
821 );
822 assert_eq!(
823 parser.attribute_value("firstname"),
824 InterpretedValue::Value("Αλέξανδρος")
825 );
826 assert_eq!(
827 parser.attribute_value("middlename"),
828 InterpretedValue::Value("Μ.")
829 );
830 assert_eq!(
831 parser.attribute_value("lastname"),
832 InterpretedValue::Value("Παπαδόπουλος")
833 );
834 assert_eq!(
835 parser.attribute_value("authorinitials"),
836 InterpretedValue::Value("ΑΜΠ")
837 );
838 }
839
840 #[test]
841 fn semicolon_in_character_reference_not_treated_as_separator() {
842 let mut parser = Parser::default();
843
844 let al = crate::document::AuthorLine::parse(
845 crate::Span::new("AsciiDoc®{empty} WG; Another Author"),
846 &mut parser,
847 );
848
849 assert_eq!(
850 al,
851 AuthorLine {
852 authors: &[
853 Author {
854 name: "AsciiDoc® WG",
855 firstname: "AsciiDoc®",
856 middlename: None,
857 lastname: Some("WG"),
858 email: None,
859 },
860 Author {
861 name: "Another Author",
862 firstname: "Another",
863 middlename: None,
864 lastname: Some("Author"),
865 email: None,
866 },
867 ],
868 source: Span {
869 data: "AsciiDoc®{empty} WG; Another Author",
870 line: 1,
871 col: 1,
872 offset: 0,
873 },
874 }
875 );
876 }
877
878 #[test]
879 fn comprehensive_author_attribute_test() {
880 let mut parser = Parser::default();
884 let doc = parser.parse("= Document Title\nFirst Second Last <first@example.com>; Only First; A B C <abc@example.com>; No Email Guy");
885
886 assert_eq!(
887 parser.attribute_value("author"),
888 InterpretedValue::Value("First Second Last")
889 );
890
891 assert_eq!(
892 parser.attribute_value("firstname"),
893 InterpretedValue::Value("First")
894 );
895
896 assert_eq!(
897 parser.attribute_value("middlename"),
898 InterpretedValue::Value("Second")
899 );
900
901 assert_eq!(
902 parser.attribute_value("lastname"),
903 InterpretedValue::Value("Last")
904 );
905
906 assert_eq!(
907 parser.attribute_value("authorinitials"),
908 InterpretedValue::Value("FSL")
909 );
910
911 assert_eq!(
912 parser.attribute_value("email"),
913 InterpretedValue::Value("first@example.com")
914 );
915
916 assert_eq!(
917 parser.attribute_value("author_2"),
918 InterpretedValue::Value("Only First")
919 );
920
921 assert_eq!(
922 parser.attribute_value("firstname_2"),
923 InterpretedValue::Value("Only")
924 );
925
926 assert_eq!(
927 parser.attribute_value("middlename_2"),
928 InterpretedValue::Unset
929 );
930
931 assert_eq!(
932 parser.attribute_value("lastname_2"),
933 InterpretedValue::Value("First")
934 );
935
936 assert_eq!(
937 parser.attribute_value("authorinitials_2"),
938 InterpretedValue::Value("OF")
939 );
940
941 assert_eq!(parser.attribute_value("email_2"), InterpretedValue::Unset);
942
943 assert_eq!(
944 parser.attribute_value("author_3"),
945 InterpretedValue::Value("A B C")
946 );
947
948 assert_eq!(
949 parser.attribute_value("firstname_3"),
950 InterpretedValue::Value("A")
951 );
952
953 assert_eq!(
954 parser.attribute_value("middlename_3"),
955 InterpretedValue::Value("B")
956 );
957
958 assert_eq!(
959 parser.attribute_value("lastname_3"),
960 InterpretedValue::Value("C")
961 );
962
963 assert_eq!(
964 parser.attribute_value("authorinitials_3"),
965 InterpretedValue::Value("ABC")
966 );
967
968 assert_eq!(
969 parser.attribute_value("email_3"),
970 InterpretedValue::Value("abc@example.com")
971 );
972
973 assert_eq!(
974 parser.attribute_value("author_4"),
975 InterpretedValue::Value("No Email Guy")
976 );
977
978 assert_eq!(
979 parser.attribute_value("firstname_4"),
980 InterpretedValue::Value("No")
981 );
982
983 assert_eq!(
984 parser.attribute_value("middlename_4"),
985 InterpretedValue::Value("Email")
986 );
987
988 assert_eq!(
989 parser.attribute_value("lastname_4"),
990 InterpretedValue::Value("Guy")
991 );
992
993 assert_eq!(
994 parser.attribute_value("authorinitials_4"),
995 InterpretedValue::Value("NEG")
996 );
997
998 assert_eq!(parser.attribute_value("email_4"), InterpretedValue::Unset);
999
1000 assert_eq!(
1001 doc,
1002 Document {
1003 header: Header {
1004 title_source: Some(Span {
1005 data: "Document Title",
1006 line: 1,
1007 col: 3,
1008 offset: 2,
1009 },),
1010 title: Some("Document Title",),
1011 attributes: &[],
1012 author_line: Some(AuthorLine {
1013 authors: &[
1014 Author {
1015 name: "First Second Last",
1016 firstname: "First",
1017 middlename: Some("Second",),
1018 lastname: Some("Last",),
1019 email: Some("first@example.com",),
1020 },
1021 Author {
1022 name: "Only First",
1023 firstname: "Only",
1024 middlename: None,
1025 lastname: Some("First",),
1026 email: None,
1027 },
1028 Author {
1029 name: "A B C",
1030 firstname: "A",
1031 middlename: Some("B",),
1032 lastname: Some("C",),
1033 email: Some("abc@example.com",),
1034 },
1035 Author {
1036 name: "No Email Guy",
1037 firstname: "No",
1038 middlename: Some("Email",),
1039 lastname: Some("Guy",),
1040 email: None,
1041 },
1042 ],
1043 source: Span {
1044 data: "First Second Last <first@example.com>; Only First; A B C <abc@example.com>; No Email Guy",
1045 line: 2,
1046 col: 1,
1047 offset: 17,
1048 },
1049 },),
1050 revision_line: None,
1051 comments: &[],
1052 source: Span {
1053 data: "= Document Title\nFirst Second Last <first@example.com>; Only First; A B C <abc@example.com>; No Email Guy",
1054 line: 1,
1055 col: 1,
1056 offset: 0,
1057 },
1058 },
1059 blocks: &[],
1060 source: Span {
1061 data: "= Document Title\nFirst Second Last <first@example.com>; Only First; A B C <abc@example.com>; No Email Guy",
1062 line: 1,
1063 col: 1,
1064 offset: 0,
1065 },
1066 warnings: &[],
1067 source_map: SourceMap(&[]),
1068 catalog: Catalog::default(),
1069 }
1070 );
1071 }
1072}