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