1use super::arena::NodeId;
4use crate::properties::PropertyList;
5
6pub struct FoNode<'a> {
8 pub data: FoNodeData<'a>,
10
11 pub id: Option<String>,
14
15 pub parent: Option<NodeId>,
17
18 pub first_child: Option<NodeId>,
20
21 pub next_sibling: Option<NodeId>,
23}
24
25impl<'a> FoNode<'a> {
26 pub fn new(data: FoNodeData<'a>) -> Self {
28 Self {
29 data,
30 id: None,
31 parent: None,
32 first_child: None,
33 next_sibling: None,
34 }
35 }
36
37 pub fn new_with_id(data: FoNodeData<'a>, id: Option<String>) -> Self {
39 Self {
40 data,
41 id,
42 parent: None,
43 first_child: None,
44 next_sibling: None,
45 }
46 }
47
48 pub fn id(&self) -> Option<&str> {
50 self.id.as_deref()
51 }
52
53 pub fn set_id(&mut self, id: Option<String>) {
55 self.id = id;
56 }
57
58 #[inline]
60 pub fn has_children(&self) -> bool {
61 self.first_child.is_some()
62 }
63
64 #[inline]
66 pub fn is_root(&self) -> bool {
67 self.parent.is_none()
68 }
69}
70
71pub enum FoNodeData<'a> {
73 Root,
75
76 LayoutMasterSet,
78
79 SimplePageMaster {
81 master_name: String,
82 properties: PropertyList<'a>,
83 },
84
85 RepeatablePageMasterAlternatives {
87 maximum_repeats: Option<i32>,
89 },
90
91 ConditionalPageMasterReference {
93 master_reference: String,
95 page_position: PagePosition,
97 odd_or_even: OddOrEven,
99 blank_or_not_blank: BlankOrNotBlank,
101 },
102
103 RegionBody { properties: PropertyList<'a> },
105
106 RegionBefore { properties: PropertyList<'a> },
108
109 RegionAfter { properties: PropertyList<'a> },
111
112 RegionStart { properties: PropertyList<'a> },
114
115 RegionEnd { properties: PropertyList<'a> },
117
118 PageSequence {
120 master_reference: String,
121 format: String,
122 grouping_separator: Option<char>,
123 grouping_size: Option<usize>,
124 properties: PropertyList<'a>,
125 },
126
127 Flow {
129 flow_name: String,
130 properties: PropertyList<'a>,
131 },
132
133 StaticContent {
135 flow_name: String,
136 properties: PropertyList<'a>,
137 },
138
139 Block { properties: PropertyList<'a> },
141
142 Inline { properties: PropertyList<'a> },
144
145 ListBlock { properties: PropertyList<'a> },
147
148 ListItem { properties: PropertyList<'a> },
150
151 ListItemLabel { properties: PropertyList<'a> },
153
154 ListItemBody { properties: PropertyList<'a> },
156
157 Table { properties: PropertyList<'a> },
159
160 TableColumn { properties: PropertyList<'a> },
162
163 TableHeader { properties: PropertyList<'a> },
165
166 TableFooter { properties: PropertyList<'a> },
168
169 TableBody { properties: PropertyList<'a> },
171
172 TableRow { properties: PropertyList<'a> },
174
175 TableCell { properties: PropertyList<'a> },
177
178 ExternalGraphic {
180 src: String,
181 content_width: Option<String>,
183 content_height: Option<String>,
185 scaling: Option<String>,
187 properties: PropertyList<'a>,
188 },
189
190 InstreamForeignObject {
192 content_width: Option<String>,
193 content_height: Option<String>,
194 scaling: Option<String>,
195 foreign_xml: String,
196 properties: PropertyList<'a>,
197 },
198
199 BasicLink {
201 internal_destination: Option<String>,
202 external_destination: Option<String>,
203 properties: PropertyList<'a>,
204 },
205
206 BookmarkTree { properties: PropertyList<'a> },
208
209 Bookmark {
211 internal_destination: Option<String>,
212 external_destination: Option<String>,
213 properties: PropertyList<'a>,
214 },
215
216 BookmarkTitle { properties: PropertyList<'a> },
218
219 PageNumberCitation {
221 ref_id: String,
222 properties: PropertyList<'a>,
223 },
224
225 PageNumberCitationLast {
227 ref_id: String,
228 properties: PropertyList<'a>,
229 },
230
231 Leader { properties: PropertyList<'a> },
233
234 Marker {
236 marker_class_name: String,
237 properties: PropertyList<'a>,
238 },
239
240 RetrieveMarker {
242 retrieve_class_name: String,
243 retrieve_position: RetrievePosition,
244 properties: PropertyList<'a>,
245 },
246
247 Footnote { properties: PropertyList<'a> },
249
250 FootnoteBody { properties: PropertyList<'a> },
252
253 Float { properties: PropertyList<'a> },
255
256 PageNumber { properties: PropertyList<'a> },
258
259 BlockContainer { properties: PropertyList<'a> },
261
262 InlineContainer { properties: PropertyList<'a> },
264
265 TableAndCaption { properties: PropertyList<'a> },
267
268 TableCaption { properties: PropertyList<'a> },
270
271 PageSequenceMaster { master_name: String },
273
274 Declarations,
276
277 ColorProfile {
279 src: String,
280 color_profile_name: String,
281 },
282
283 UnsupportedElement {
285 element_name: String,
287 },
288
289 MultiSwitch { properties: PropertyList<'a> },
291
292 MultiCase {
294 starting_state: String,
296 properties: PropertyList<'a>,
297 },
298
299 MultiToggle { properties: PropertyList<'a> },
301
302 MultiProperties { properties: PropertyList<'a> },
304
305 MultiPropertySet { properties: PropertyList<'a> },
307
308 Wrapper { properties: PropertyList<'a> },
310
311 Character {
313 character: char,
315 properties: PropertyList<'a>,
316 },
317
318 BidiOverride {
320 direction: String,
322 properties: PropertyList<'a>,
323 },
324
325 InitialPropertySet { properties: PropertyList<'a> },
327
328 ChangeBarBegin {
330 change_bar_class: String,
332 properties: PropertyList<'a>,
333 },
334
335 ChangeBarEnd {
337 change_bar_class: String,
339 },
340
341 Text(String),
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq)]
347pub enum RetrievePosition {
348 FirstStartingWithinPage,
350 FirstIncludingCarryover,
352 LastStartingWithinPage,
354 LastEndingWithinPage,
356}
357
358#[derive(Debug, Clone, Copy, PartialEq, Eq)]
360pub enum PagePosition {
361 First,
363 Last,
365 Rest,
367 Any,
369}
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub enum OddOrEven {
374 Odd,
376 Even,
378 Any,
380}
381
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384pub enum BlankOrNotBlank {
385 Blank,
387 NotBlank,
389 Any,
391}
392
393impl<'a> FoNodeData<'a> {
394 pub fn element_name(&self) -> &str {
396 match self {
397 FoNodeData::Root => "root",
398 FoNodeData::LayoutMasterSet => "layout-master-set",
399 FoNodeData::SimplePageMaster { .. } => "simple-page-master",
400 FoNodeData::RepeatablePageMasterAlternatives { .. } => {
401 "repeatable-page-master-alternatives"
402 }
403 FoNodeData::ConditionalPageMasterReference { .. } => {
404 "conditional-page-master-reference"
405 }
406 FoNodeData::RegionBody { .. } => "region-body",
407 FoNodeData::RegionBefore { .. } => "region-before",
408 FoNodeData::RegionAfter { .. } => "region-after",
409 FoNodeData::RegionStart { .. } => "region-start",
410 FoNodeData::RegionEnd { .. } => "region-end",
411 FoNodeData::PageSequence { .. } => "page-sequence",
412 FoNodeData::Flow { .. } => "flow",
413 FoNodeData::StaticContent { .. } => "static-content",
414 FoNodeData::Block { .. } => "block",
415 FoNodeData::Inline { .. } => "inline",
416 FoNodeData::ListBlock { .. } => "list-block",
417 FoNodeData::ListItem { .. } => "list-item",
418 FoNodeData::ListItemLabel { .. } => "list-item-label",
419 FoNodeData::ListItemBody { .. } => "list-item-body",
420 FoNodeData::Table { .. } => "table",
421 FoNodeData::TableColumn { .. } => "table-column",
422 FoNodeData::TableHeader { .. } => "table-header",
423 FoNodeData::TableFooter { .. } => "table-footer",
424 FoNodeData::TableBody { .. } => "table-body",
425 FoNodeData::TableRow { .. } => "table-row",
426 FoNodeData::TableCell { .. } => "table-cell",
427 FoNodeData::ExternalGraphic { .. } => "external-graphic",
428 FoNodeData::InstreamForeignObject { .. } => "instream-foreign-object",
429 FoNodeData::BasicLink { .. } => "basic-link",
430 FoNodeData::BookmarkTree { .. } => "bookmark-tree",
431 FoNodeData::Bookmark { .. } => "bookmark",
432 FoNodeData::BookmarkTitle { .. } => "bookmark-title",
433 FoNodeData::PageNumberCitation { .. } => "page-number-citation",
434 FoNodeData::PageNumberCitationLast { .. } => "page-number-citation-last",
435 FoNodeData::Leader { .. } => "leader",
436 FoNodeData::Marker { .. } => "marker",
437 FoNodeData::RetrieveMarker { .. } => "retrieve-marker",
438 FoNodeData::Footnote { .. } => "footnote",
439 FoNodeData::FootnoteBody { .. } => "footnote-body",
440 FoNodeData::Float { .. } => "float",
441 FoNodeData::PageNumber { .. } => "page-number",
442 FoNodeData::BlockContainer { .. } => "block-container",
443 FoNodeData::InlineContainer { .. } => "inline-container",
444 FoNodeData::TableAndCaption { .. } => "table-and-caption",
445 FoNodeData::TableCaption { .. } => "table-caption",
446 FoNodeData::PageSequenceMaster { .. } => "page-sequence-master",
447 FoNodeData::Declarations => "declarations",
448 FoNodeData::ColorProfile { .. } => "color-profile",
449 FoNodeData::UnsupportedElement { .. } => "unsupported-element",
450 FoNodeData::MultiSwitch { .. } => "multi-switch",
451 FoNodeData::MultiCase { .. } => "multi-case",
452 FoNodeData::MultiToggle { .. } => "multi-toggle",
453 FoNodeData::MultiProperties { .. } => "multi-properties",
454 FoNodeData::MultiPropertySet { .. } => "multi-property-set",
455 FoNodeData::Wrapper { .. } => "wrapper",
456 FoNodeData::Character { .. } => "character",
457 FoNodeData::BidiOverride { .. } => "bidi-override",
458 FoNodeData::InitialPropertySet { .. } => "initial-property-set",
459 FoNodeData::ChangeBarBegin { .. } => "change-bar-begin",
460 FoNodeData::ChangeBarEnd { .. } => "change-bar-end",
461 FoNodeData::Text(_) => "#text",
462 }
463 }
464
465 pub fn properties_mut(&mut self) -> Option<&mut PropertyList<'a>> {
467 match self {
468 FoNodeData::SimplePageMaster { properties, .. }
469 | FoNodeData::RegionBody { properties }
470 | FoNodeData::RegionBefore { properties }
471 | FoNodeData::RegionAfter { properties }
472 | FoNodeData::RegionStart { properties }
473 | FoNodeData::RegionEnd { properties }
474 | FoNodeData::PageSequence { properties, .. }
475 | FoNodeData::Flow { properties, .. }
476 | FoNodeData::StaticContent { properties, .. }
477 | FoNodeData::Block { properties }
478 | FoNodeData::Inline { properties }
479 | FoNodeData::ListBlock { properties }
480 | FoNodeData::ListItem { properties }
481 | FoNodeData::ListItemLabel { properties }
482 | FoNodeData::ListItemBody { properties }
483 | FoNodeData::Table { properties }
484 | FoNodeData::TableColumn { properties }
485 | FoNodeData::TableHeader { properties }
486 | FoNodeData::TableFooter { properties }
487 | FoNodeData::TableBody { properties }
488 | FoNodeData::TableRow { properties }
489 | FoNodeData::TableCell { properties }
490 | FoNodeData::ExternalGraphic { properties, .. }
491 | FoNodeData::InstreamForeignObject { properties, .. }
492 | FoNodeData::BasicLink { properties, .. }
493 | FoNodeData::BookmarkTree { properties }
494 | FoNodeData::Bookmark { properties, .. }
495 | FoNodeData::BookmarkTitle { properties }
496 | FoNodeData::PageNumberCitation { properties, .. }
497 | FoNodeData::PageNumberCitationLast { properties, .. }
498 | FoNodeData::Leader { properties }
499 | FoNodeData::Marker { properties, .. }
500 | FoNodeData::RetrieveMarker { properties, .. }
501 | FoNodeData::Footnote { properties }
502 | FoNodeData::FootnoteBody { properties }
503 | FoNodeData::Float { properties }
504 | FoNodeData::Wrapper { properties }
505 | FoNodeData::Character { properties, .. }
506 | FoNodeData::BidiOverride { properties, .. }
507 | FoNodeData::InitialPropertySet { properties }
508 | FoNodeData::PageNumber { properties }
509 | FoNodeData::BlockContainer { properties }
510 | FoNodeData::InlineContainer { properties }
511 | FoNodeData::TableAndCaption { properties }
512 | FoNodeData::TableCaption { properties }
513 | FoNodeData::MultiSwitch { properties }
514 | FoNodeData::MultiCase { properties, .. }
515 | FoNodeData::MultiToggle { properties }
516 | FoNodeData::MultiProperties { properties }
517 | FoNodeData::MultiPropertySet { properties }
518 | FoNodeData::ChangeBarBegin { properties, .. } => Some(properties),
519 FoNodeData::ChangeBarEnd { .. } => None,
520 _ => None,
521 }
522 }
523
524 pub fn properties(&self) -> Option<&PropertyList<'a>> {
526 match self {
527 FoNodeData::SimplePageMaster { properties, .. }
528 | FoNodeData::RegionBody { properties }
529 | FoNodeData::RegionBefore { properties }
530 | FoNodeData::RegionAfter { properties }
531 | FoNodeData::RegionStart { properties }
532 | FoNodeData::RegionEnd { properties }
533 | FoNodeData::PageSequence { properties, .. }
534 | FoNodeData::Flow { properties, .. }
535 | FoNodeData::StaticContent { properties, .. }
536 | FoNodeData::Block { properties }
537 | FoNodeData::Inline { properties }
538 | FoNodeData::ListBlock { properties }
539 | FoNodeData::ListItem { properties }
540 | FoNodeData::ListItemLabel { properties }
541 | FoNodeData::ListItemBody { properties }
542 | FoNodeData::Table { properties }
543 | FoNodeData::TableColumn { properties }
544 | FoNodeData::TableHeader { properties }
545 | FoNodeData::TableFooter { properties }
546 | FoNodeData::TableBody { properties }
547 | FoNodeData::TableRow { properties }
548 | FoNodeData::TableCell { properties }
549 | FoNodeData::ExternalGraphic { properties, .. }
550 | FoNodeData::InstreamForeignObject { properties, .. }
551 | FoNodeData::BasicLink { properties, .. }
552 | FoNodeData::BookmarkTree { properties }
553 | FoNodeData::Bookmark { properties, .. }
554 | FoNodeData::BookmarkTitle { properties }
555 | FoNodeData::PageNumberCitation { properties, .. }
556 | FoNodeData::PageNumberCitationLast { properties, .. }
557 | FoNodeData::Leader { properties }
558 | FoNodeData::Marker { properties, .. }
559 | FoNodeData::RetrieveMarker { properties, .. }
560 | FoNodeData::Footnote { properties }
561 | FoNodeData::FootnoteBody { properties }
562 | FoNodeData::Float { properties }
563 | FoNodeData::Wrapper { properties }
564 | FoNodeData::Character { properties, .. }
565 | FoNodeData::BidiOverride { properties, .. }
566 | FoNodeData::InitialPropertySet { properties }
567 | FoNodeData::PageNumber { properties }
568 | FoNodeData::BlockContainer { properties }
569 | FoNodeData::InlineContainer { properties }
570 | FoNodeData::TableAndCaption { properties }
571 | FoNodeData::TableCaption { properties }
572 | FoNodeData::MultiSwitch { properties }
573 | FoNodeData::MultiCase { properties, .. }
574 | FoNodeData::MultiToggle { properties }
575 | FoNodeData::MultiProperties { properties }
576 | FoNodeData::MultiPropertySet { properties }
577 | FoNodeData::ChangeBarBegin { properties, .. } => Some(properties),
578 FoNodeData::ChangeBarEnd { .. } => None,
579 _ => None,
580 }
581 }
582
583 pub fn can_contain_text(&self) -> bool {
585 matches!(
586 self,
587 FoNodeData::Block { .. }
588 | FoNodeData::Inline { .. }
589 | FoNodeData::BasicLink { .. }
590 | FoNodeData::BookmarkTitle { .. }
591 | FoNodeData::Marker { .. }
592 | FoNodeData::FootnoteBody { .. }
593 )
594 }
595
596 pub fn is_layout_master(&self) -> bool {
598 matches!(
599 self,
600 FoNodeData::LayoutMasterSet
601 | FoNodeData::SimplePageMaster { .. }
602 | FoNodeData::RepeatablePageMasterAlternatives { .. }
603 | FoNodeData::ConditionalPageMasterReference { .. }
604 )
605 }
606
607 pub fn is_region(&self) -> bool {
609 matches!(
610 self,
611 FoNodeData::RegionBody { .. }
612 | FoNodeData::RegionBefore { .. }
613 | FoNodeData::RegionAfter { .. }
614 | FoNodeData::RegionStart { .. }
615 | FoNodeData::RegionEnd { .. }
616 )
617 }
618}
619
620#[cfg(test)]
621mod tests {
622 use super::*;
623
624 #[test]
625 fn test_node_creation() {
626 let node = FoNode::new(FoNodeData::Root);
627 assert!(node.is_root());
628 assert!(!node.has_children());
629 }
630
631 #[test]
632 fn test_element_names() {
633 assert_eq!(FoNodeData::Root.element_name(), "root");
634 assert_eq!(
635 FoNodeData::LayoutMasterSet.element_name(),
636 "layout-master-set"
637 );
638 assert_eq!(FoNodeData::Text(String::new()).element_name(), "#text");
639 }
640
641 #[test]
642 fn test_property_access() {
643 let props = PropertyList::new();
644 let mut data = FoNodeData::Block { properties: props };
645
646 assert!(data.properties().is_some());
647 assert!(data.properties_mut().is_some());
648
649 let root = FoNodeData::Root;
650 assert!(root.properties().is_none());
651 }
652
653 #[test]
654 fn test_node_type_checks() {
655 let layout = FoNodeData::LayoutMasterSet;
656 assert!(layout.is_layout_master());
657
658 let region = FoNodeData::RegionBody {
659 properties: PropertyList::new(),
660 };
661 assert!(region.is_region());
662
663 let block = FoNodeData::Block {
664 properties: PropertyList::new(),
665 };
666 assert!(block.can_contain_text());
667 }
668}
669
670#[cfg(test)]
671mod tests_extended {
672 use super::*;
673
674 fn props() -> PropertyList<'static> {
675 PropertyList::new()
676 }
677
678 #[test]
683 fn test_element_name_root() {
684 assert_eq!(FoNodeData::Root.element_name(), "root");
685 }
686
687 #[test]
688 fn test_element_name_layout_master_set() {
689 assert_eq!(
690 FoNodeData::LayoutMasterSet.element_name(),
691 "layout-master-set"
692 );
693 }
694
695 #[test]
696 fn test_element_name_simple_page_master() {
697 let data = FoNodeData::SimplePageMaster {
698 master_name: "A4".to_string(),
699 properties: props(),
700 };
701 assert_eq!(data.element_name(), "simple-page-master");
702 }
703
704 #[test]
705 fn test_element_name_page_sequence() {
706 let data = FoNodeData::PageSequence {
707 master_reference: "A4".to_string(),
708 format: "1".to_string(),
709 grouping_separator: None,
710 grouping_size: None,
711 properties: props(),
712 };
713 assert_eq!(data.element_name(), "page-sequence");
714 }
715
716 #[test]
717 fn test_element_name_flow() {
718 let data = FoNodeData::Flow {
719 flow_name: "xsl-region-body".to_string(),
720 properties: props(),
721 };
722 assert_eq!(data.element_name(), "flow");
723 }
724
725 #[test]
726 fn test_element_name_static_content() {
727 let data = FoNodeData::StaticContent {
728 flow_name: "xsl-region-before".to_string(),
729 properties: props(),
730 };
731 assert_eq!(data.element_name(), "static-content");
732 }
733
734 #[test]
735 fn test_element_name_block() {
736 assert_eq!(
737 FoNodeData::Block {
738 properties: props()
739 }
740 .element_name(),
741 "block"
742 );
743 }
744
745 #[test]
746 fn test_element_name_inline() {
747 assert_eq!(
748 FoNodeData::Inline {
749 properties: props()
750 }
751 .element_name(),
752 "inline"
753 );
754 }
755
756 #[test]
757 fn test_element_name_list_elements() {
758 assert_eq!(
759 FoNodeData::ListBlock {
760 properties: props()
761 }
762 .element_name(),
763 "list-block"
764 );
765 assert_eq!(
766 FoNodeData::ListItem {
767 properties: props()
768 }
769 .element_name(),
770 "list-item"
771 );
772 assert_eq!(
773 FoNodeData::ListItemLabel {
774 properties: props()
775 }
776 .element_name(),
777 "list-item-label"
778 );
779 assert_eq!(
780 FoNodeData::ListItemBody {
781 properties: props()
782 }
783 .element_name(),
784 "list-item-body"
785 );
786 }
787
788 #[test]
789 fn test_element_name_table_elements() {
790 assert_eq!(
791 FoNodeData::Table {
792 properties: props()
793 }
794 .element_name(),
795 "table"
796 );
797 assert_eq!(
798 FoNodeData::TableColumn {
799 properties: props()
800 }
801 .element_name(),
802 "table-column"
803 );
804 assert_eq!(
805 FoNodeData::TableHeader {
806 properties: props()
807 }
808 .element_name(),
809 "table-header"
810 );
811 assert_eq!(
812 FoNodeData::TableFooter {
813 properties: props()
814 }
815 .element_name(),
816 "table-footer"
817 );
818 assert_eq!(
819 FoNodeData::TableBody {
820 properties: props()
821 }
822 .element_name(),
823 "table-body"
824 );
825 assert_eq!(
826 FoNodeData::TableRow {
827 properties: props()
828 }
829 .element_name(),
830 "table-row"
831 );
832 assert_eq!(
833 FoNodeData::TableCell {
834 properties: props()
835 }
836 .element_name(),
837 "table-cell"
838 );
839 }
840
841 #[test]
842 fn test_element_name_graphic() {
843 let data = FoNodeData::ExternalGraphic {
844 src: "img.png".to_string(),
845 content_width: None,
846 content_height: None,
847 scaling: None,
848 properties: props(),
849 };
850 assert_eq!(data.element_name(), "external-graphic");
851 }
852
853 #[test]
854 fn test_element_name_basic_link() {
855 let data = FoNodeData::BasicLink {
856 internal_destination: None,
857 external_destination: Some("http://example.com".to_string()),
858 properties: props(),
859 };
860 assert_eq!(data.element_name(), "basic-link");
861 }
862
863 #[test]
864 fn test_element_name_page_number() {
865 assert_eq!(
866 FoNodeData::PageNumber {
867 properties: props()
868 }
869 .element_name(),
870 "page-number"
871 );
872 }
873
874 #[test]
875 fn test_element_name_page_number_citation() {
876 let data = FoNodeData::PageNumberCitation {
877 ref_id: "sec1".to_string(),
878 properties: props(),
879 };
880 assert_eq!(data.element_name(), "page-number-citation");
881 }
882
883 #[test]
884 fn test_element_name_page_number_citation_last() {
885 let data = FoNodeData::PageNumberCitationLast {
886 ref_id: "sec1".to_string(),
887 properties: props(),
888 };
889 assert_eq!(data.element_name(), "page-number-citation-last");
890 }
891
892 #[test]
893 fn test_element_name_block_container() {
894 assert_eq!(
895 FoNodeData::BlockContainer {
896 properties: props()
897 }
898 .element_name(),
899 "block-container"
900 );
901 }
902
903 #[test]
904 fn test_element_name_inline_container() {
905 assert_eq!(
906 FoNodeData::InlineContainer {
907 properties: props()
908 }
909 .element_name(),
910 "inline-container"
911 );
912 }
913
914 #[test]
915 fn test_element_name_wrapper() {
916 assert_eq!(
917 FoNodeData::Wrapper {
918 properties: props()
919 }
920 .element_name(),
921 "wrapper"
922 );
923 }
924
925 #[test]
926 fn test_element_name_character() {
927 let data = FoNodeData::Character {
928 character: 'A',
929 properties: props(),
930 };
931 assert_eq!(data.element_name(), "character");
932 }
933
934 #[test]
935 fn test_element_name_bidi_override() {
936 let data = FoNodeData::BidiOverride {
937 direction: "rtl".to_string(),
938 properties: props(),
939 };
940 assert_eq!(data.element_name(), "bidi-override");
941 }
942
943 #[test]
944 fn test_element_name_initial_property_set() {
945 assert_eq!(
946 FoNodeData::InitialPropertySet {
947 properties: props()
948 }
949 .element_name(),
950 "initial-property-set"
951 );
952 }
953
954 #[test]
955 fn test_element_name_declarations() {
956 assert_eq!(FoNodeData::Declarations.element_name(), "declarations");
957 }
958
959 #[test]
960 fn test_element_name_color_profile() {
961 let data = FoNodeData::ColorProfile {
962 src: "profile.icc".to_string(),
963 color_profile_name: "cmyk".to_string(),
964 };
965 assert_eq!(data.element_name(), "color-profile");
966 }
967
968 #[test]
969 fn test_element_name_unsupported() {
970 let data = FoNodeData::UnsupportedElement {
971 element_name: "fo:multi-properties".to_string(),
972 };
973 assert_eq!(data.element_name(), "unsupported-element");
974 }
975
976 #[test]
977 fn test_element_name_text() {
978 assert_eq!(
979 FoNodeData::Text("hello".to_string()).element_name(),
980 "#text"
981 );
982 }
983
984 #[test]
985 fn test_element_name_leader() {
986 assert_eq!(
987 FoNodeData::Leader {
988 properties: props()
989 }
990 .element_name(),
991 "leader"
992 );
993 }
994
995 #[test]
996 fn test_element_name_footnote() {
997 assert_eq!(
998 FoNodeData::Footnote {
999 properties: props()
1000 }
1001 .element_name(),
1002 "footnote"
1003 );
1004 assert_eq!(
1005 FoNodeData::FootnoteBody {
1006 properties: props()
1007 }
1008 .element_name(),
1009 "footnote-body"
1010 );
1011 }
1012
1013 #[test]
1014 fn test_element_name_bookmark() {
1015 assert_eq!(
1016 FoNodeData::BookmarkTree {
1017 properties: props()
1018 }
1019 .element_name(),
1020 "bookmark-tree"
1021 );
1022 let bm = FoNodeData::Bookmark {
1023 internal_destination: Some("intro".to_string()),
1024 external_destination: None,
1025 properties: props(),
1026 };
1027 assert_eq!(bm.element_name(), "bookmark");
1028 assert_eq!(
1029 FoNodeData::BookmarkTitle {
1030 properties: props()
1031 }
1032 .element_name(),
1033 "bookmark-title"
1034 );
1035 }
1036
1037 #[test]
1038 fn test_element_name_marker() {
1039 let data = FoNodeData::Marker {
1040 marker_class_name: "header".to_string(),
1041 properties: props(),
1042 };
1043 assert_eq!(data.element_name(), "marker");
1044 }
1045
1046 #[test]
1047 fn test_element_name_retrieve_marker() {
1048 let data = FoNodeData::RetrieveMarker {
1049 retrieve_class_name: "header".to_string(),
1050 retrieve_position: RetrievePosition::FirstStartingWithinPage,
1051 properties: props(),
1052 };
1053 assert_eq!(data.element_name(), "retrieve-marker");
1054 }
1055
1056 #[test]
1057 fn test_element_name_page_sequence_master() {
1058 let data = FoNodeData::PageSequenceMaster {
1059 master_name: "alternating".to_string(),
1060 };
1061 assert_eq!(data.element_name(), "page-sequence-master");
1062 }
1063
1064 #[test]
1065 fn test_element_name_change_bar() {
1066 let begin = FoNodeData::ChangeBarBegin {
1067 change_bar_class: "c1".to_string(),
1068 properties: props(),
1069 };
1070 let end = FoNodeData::ChangeBarEnd {
1071 change_bar_class: "c1".to_string(),
1072 };
1073 assert_eq!(begin.element_name(), "change-bar-begin");
1074 assert_eq!(end.element_name(), "change-bar-end");
1075 }
1076
1077 #[test]
1082 fn test_fo_node_new_with_id() {
1083 let node = FoNode::new_with_id(FoNodeData::Root, Some("root-id".to_string()));
1084 assert_eq!(node.id(), Some("root-id"));
1085 assert!(node.is_root());
1086 }
1087
1088 #[test]
1089 fn test_fo_node_new_no_id() {
1090 let node = FoNode::new(FoNodeData::LayoutMasterSet);
1091 assert!(node.id().is_none());
1092 }
1093
1094 #[test]
1095 fn test_fo_node_set_id() {
1096 let mut node = FoNode::new(FoNodeData::Root);
1097 assert!(node.id().is_none());
1098 node.set_id(Some("my-id".to_string()));
1099 assert_eq!(node.id(), Some("my-id"));
1100 node.set_id(None);
1101 assert!(node.id().is_none());
1102 }
1103
1104 #[test]
1105 fn test_fo_node_has_children() {
1106 use super::super::arena::NodeId;
1107 let mut node = FoNode::new(FoNodeData::Root);
1108 assert!(!node.has_children());
1109 node.first_child = Some(NodeId::from_index(1));
1111 assert!(node.has_children());
1112 }
1113
1114 #[test]
1115 fn test_fo_node_is_root_with_parent() {
1116 use super::super::arena::NodeId;
1117 let mut node = FoNode::new(FoNodeData::Block {
1118 properties: props(),
1119 });
1120 assert!(node.is_root()); node.parent = Some(NodeId::from_index(0));
1122 assert!(!node.is_root());
1123 }
1124
1125 #[test]
1130 fn test_can_contain_text_block_level() {
1131 assert!(FoNodeData::Block {
1132 properties: props()
1133 }
1134 .can_contain_text());
1135 assert!(FoNodeData::Inline {
1136 properties: props()
1137 }
1138 .can_contain_text());
1139 }
1140
1141 #[test]
1142 fn test_cannot_contain_text_non_text_containers() {
1143 assert!(!FoNodeData::Table {
1144 properties: props()
1145 }
1146 .can_contain_text());
1147 assert!(!FoNodeData::TableRow {
1148 properties: props()
1149 }
1150 .can_contain_text());
1151 assert!(!FoNodeData::Root.can_contain_text());
1152 assert!(!FoNodeData::LayoutMasterSet.can_contain_text());
1153 assert!(!FoNodeData::ListBlock {
1154 properties: props()
1155 }
1156 .can_contain_text());
1157 }
1158
1159 #[test]
1160 fn test_is_layout_master_variants() {
1161 assert!(FoNodeData::LayoutMasterSet.is_layout_master());
1162 assert!(FoNodeData::SimplePageMaster {
1163 master_name: "A4".to_string(),
1164 properties: props(),
1165 }
1166 .is_layout_master());
1167 assert!(FoNodeData::RepeatablePageMasterAlternatives {
1168 maximum_repeats: None,
1169 }
1170 .is_layout_master());
1171 assert!(FoNodeData::ConditionalPageMasterReference {
1172 master_reference: "A4".to_string(),
1173 page_position: PagePosition::Any,
1174 odd_or_even: OddOrEven::Any,
1175 blank_or_not_blank: BlankOrNotBlank::Any,
1176 }
1177 .is_layout_master());
1178 }
1179
1180 #[test]
1181 fn test_is_not_layout_master() {
1182 assert!(!FoNodeData::Root.is_layout_master());
1183 assert!(!FoNodeData::Block {
1184 properties: props()
1185 }
1186 .is_layout_master());
1187 }
1188
1189 #[test]
1190 fn test_is_region_all_variants() {
1191 assert!(FoNodeData::RegionBody {
1192 properties: props()
1193 }
1194 .is_region());
1195 assert!(FoNodeData::RegionBefore {
1196 properties: props()
1197 }
1198 .is_region());
1199 assert!(FoNodeData::RegionAfter {
1200 properties: props()
1201 }
1202 .is_region());
1203 assert!(FoNodeData::RegionStart {
1204 properties: props()
1205 }
1206 .is_region());
1207 assert!(FoNodeData::RegionEnd {
1208 properties: props()
1209 }
1210 .is_region());
1211 }
1212
1213 #[test]
1214 fn test_is_not_region() {
1215 assert!(!FoNodeData::Root.is_region());
1216 assert!(!FoNodeData::Block {
1217 properties: props()
1218 }
1219 .is_region());
1220 assert!(!FoNodeData::LayoutMasterSet.is_region());
1221 }
1222
1223 #[test]
1228 fn test_properties_none_for_propertyless_variants() {
1229 assert!(FoNodeData::Root.properties().is_none());
1230 assert!(FoNodeData::LayoutMasterSet.properties().is_none());
1231 assert!(FoNodeData::Declarations.properties().is_none());
1232 assert!(FoNodeData::Text("hi".to_string()).properties().is_none());
1233 let cbe = FoNodeData::ChangeBarEnd {
1234 change_bar_class: "c1".to_string(),
1235 };
1236 assert!(cbe.properties().is_none());
1237 }
1238
1239 #[test]
1240 fn test_properties_some_for_property_bearing_variants() {
1241 assert!(FoNodeData::Block {
1242 properties: props()
1243 }
1244 .properties()
1245 .is_some());
1246 assert!(FoNodeData::Inline {
1247 properties: props()
1248 }
1249 .properties()
1250 .is_some());
1251 assert!(FoNodeData::Table {
1252 properties: props()
1253 }
1254 .properties()
1255 .is_some());
1256 assert!(FoNodeData::PageNumber {
1257 properties: props()
1258 }
1259 .properties()
1260 .is_some());
1261 assert!(FoNodeData::BlockContainer {
1262 properties: props()
1263 }
1264 .properties()
1265 .is_some());
1266 assert!(FoNodeData::Wrapper {
1267 properties: props()
1268 }
1269 .properties()
1270 .is_some());
1271 }
1272
1273 #[test]
1274 fn test_properties_mut_some() {
1275 let mut data = FoNodeData::Inline {
1276 properties: props(),
1277 };
1278 assert!(data.properties_mut().is_some());
1279 }
1280
1281 #[test]
1282 fn test_properties_mut_none_for_text() {
1283 let mut data = FoNodeData::Text("hello".to_string());
1284 assert!(data.properties_mut().is_none());
1285 }
1286
1287 #[test]
1292 fn test_page_position_equality() {
1293 assert_eq!(PagePosition::First, PagePosition::First);
1294 assert_ne!(PagePosition::First, PagePosition::Last);
1295 assert_ne!(PagePosition::Rest, PagePosition::Any);
1296 }
1297
1298 #[test]
1299 fn test_odd_or_even_equality() {
1300 assert_eq!(OddOrEven::Odd, OddOrEven::Odd);
1301 assert_ne!(OddOrEven::Odd, OddOrEven::Even);
1302 }
1303
1304 #[test]
1305 fn test_blank_or_not_blank_equality() {
1306 assert_eq!(BlankOrNotBlank::Blank, BlankOrNotBlank::Blank);
1307 assert_ne!(BlankOrNotBlank::Blank, BlankOrNotBlank::NotBlank);
1308 }
1309
1310 #[test]
1311 fn test_retrieve_position_copy() {
1312 let p1 = RetrievePosition::FirstStartingWithinPage;
1313 let p2 = p1;
1314 assert_eq!(p1, p2);
1315 }
1316
1317 #[test]
1318 fn test_region_element_names() {
1319 assert_eq!(
1320 FoNodeData::RegionBody {
1321 properties: props()
1322 }
1323 .element_name(),
1324 "region-body"
1325 );
1326 assert_eq!(
1327 FoNodeData::RegionBefore {
1328 properties: props()
1329 }
1330 .element_name(),
1331 "region-before"
1332 );
1333 assert_eq!(
1334 FoNodeData::RegionAfter {
1335 properties: props()
1336 }
1337 .element_name(),
1338 "region-after"
1339 );
1340 assert_eq!(
1341 FoNodeData::RegionStart {
1342 properties: props()
1343 }
1344 .element_name(),
1345 "region-start"
1346 );
1347 assert_eq!(
1348 FoNodeData::RegionEnd {
1349 properties: props()
1350 }
1351 .element_name(),
1352 "region-end"
1353 );
1354 }
1355
1356 #[test]
1357 fn test_multi_element_names() {
1358 assert_eq!(
1359 FoNodeData::MultiSwitch {
1360 properties: props()
1361 }
1362 .element_name(),
1363 "multi-switch"
1364 );
1365 assert_eq!(
1366 FoNodeData::MultiCase {
1367 starting_state: "visible".to_string(),
1368 properties: props(),
1369 }
1370 .element_name(),
1371 "multi-case"
1372 );
1373 assert_eq!(
1374 FoNodeData::MultiToggle {
1375 properties: props()
1376 }
1377 .element_name(),
1378 "multi-toggle"
1379 );
1380 assert_eq!(
1381 FoNodeData::MultiProperties {
1382 properties: props()
1383 }
1384 .element_name(),
1385 "multi-properties"
1386 );
1387 assert_eq!(
1388 FoNodeData::MultiPropertySet {
1389 properties: props()
1390 }
1391 .element_name(),
1392 "multi-property-set"
1393 );
1394 }
1395
1396 #[test]
1397 fn test_float_element_name() {
1398 assert_eq!(
1399 FoNodeData::Float {
1400 properties: props()
1401 }
1402 .element_name(),
1403 "float"
1404 );
1405 }
1406
1407 #[test]
1408 fn test_table_and_caption_element_names() {
1409 assert_eq!(
1410 FoNodeData::TableAndCaption {
1411 properties: props()
1412 }
1413 .element_name(),
1414 "table-and-caption"
1415 );
1416 assert_eq!(
1417 FoNodeData::TableCaption {
1418 properties: props()
1419 }
1420 .element_name(),
1421 "table-caption"
1422 );
1423 }
1424
1425 #[test]
1426 fn test_instream_foreign_object_element_name() {
1427 let data = FoNodeData::InstreamForeignObject {
1428 content_width: None,
1429 content_height: None,
1430 scaling: None,
1431 foreign_xml: "<svg/>".to_string(),
1432 properties: props(),
1433 };
1434 assert_eq!(data.element_name(), "instream-foreign-object");
1435 }
1436
1437 #[test]
1438 fn test_repeatable_page_master_alternatives_element_name() {
1439 let data = FoNodeData::RepeatablePageMasterAlternatives {
1440 maximum_repeats: Some(3),
1441 };
1442 assert_eq!(data.element_name(), "repeatable-page-master-alternatives");
1443 }
1444
1445 #[test]
1446 fn test_conditional_page_master_reference_element_name() {
1447 let data = FoNodeData::ConditionalPageMasterReference {
1448 master_reference: "A4".to_string(),
1449 page_position: PagePosition::First,
1450 odd_or_even: OddOrEven::Odd,
1451 blank_or_not_blank: BlankOrNotBlank::NotBlank,
1452 };
1453 assert_eq!(data.element_name(), "conditional-page-master-reference");
1454 }
1455}