1#[derive(Clone, Debug, PartialEq)]
14pub enum OrderItem {
15 Group(usize),
17 Text(String),
19 Array(Vec<OrderItem>),
21 Other,
25}
26
27#[derive(Clone, Debug, PartialEq)]
29pub enum LayerNode {
30 Layer { index: usize, children: Vec<LayerNode> },
33 Label { text: String, children: Vec<LayerNode> },
35 Additional(Vec<LayerNode>),
38}
39
40const MAX_LEVELS: usize = 10;
42
43pub fn layer_tree(order: &[OrderItem], groups: usize) -> Vec<LayerNode> {
46 let mut seen = vec![false; groups];
47 let mut tree = level(order, 0, &mut seen);
48 let rest: Vec<LayerNode> = (0..groups).filter(|&i| !seen[i]).map(|index| LayerNode::Layer { index, children: Vec::new() }).collect();
49 if !rest.is_empty() {
50 tree.push(LayerNode::Additional(rest));
51 }
52 tree
53}
54
55fn level(items: &[OrderItem], depth: usize, seen: &mut [bool]) -> Vec<LayerNode> {
56 let mut out: Vec<LayerNode> = Vec::new();
57 if depth > MAX_LEVELS {
58 return out;
59 }
60 let mut after_group = false;
62 for item in items {
63 let mut group = false;
64 match item {
65 OrderItem::Group(i) if *i < seen.len() => {
66 seen[*i] = true;
67 out.push(LayerNode::Layer { index: *i, children: Vec::new() });
68 group = true;
69 }
70 OrderItem::Array(inner) => match inner.first() {
71 Some(OrderItem::Text(text)) => {
72 let children = level(&inner[1..], depth + 1, seen);
73 if !children.is_empty() {
74 out.push(LayerNode::Label { text: text.clone(), children });
75 }
76 }
77 _ => {
78 let nested = level(inner, depth + 1, seen);
79 match out.last_mut() {
80 Some(LayerNode::Layer { children, .. }) if after_group => children.extend(nested),
81 _ => out.extend(nested),
82 }
83 }
84 },
85 _ => {}
86 }
87 after_group = group;
88 }
89 out
90}
91
92#[cfg(test)]
93mod tests {
94 use super::OrderItem::*;
95 use super::*;
96
97 fn layer(index: usize, children: Vec<LayerNode>) -> LayerNode {
98 LayerNode::Layer { index, children }
99 }
100
101 fn label(text: &str, children: Vec<LayerNode>) -> LayerNode {
102 LayerNode::Label { text: text.into(), children }
103 }
104
105 #[test]
106 fn groups_labels_and_children_nest() {
107 let order = [Group(0), Array(vec![Group(1), Group(2)]), Array(vec![Text("Notes".into()), Group(3)])];
108 assert_eq!(layer_tree(&order, 4), vec![layer(0, vec![layer(1, vec![]), layer(2, vec![])]), label("Notes", vec![layer(3, vec![])])]);
109 }
110
111 #[test]
112 fn groups_left_out_go_last_and_unknown_ones_are_skipped() {
113 let order = [Group(2), Group(9), Other, Array(vec![Text("Empty".into())])];
114 assert_eq!(layer_tree(&order, 3), vec![layer(2, vec![]), LayerNode::Additional(vec![layer(0, vec![]), layer(1, vec![])])]);
115 assert!(layer_tree(&[], 0).is_empty());
116 }
117
118 #[test]
119 fn an_array_after_something_skipped_is_not_its_children() {
120 let order = [Group(0), Other, Array(vec![Group(1)])];
121 assert_eq!(layer_tree(&order, 2), vec![layer(0, vec![]), layer(1, vec![])]);
122 assert_eq!(layer_tree(&[Array(vec![Group(1)])], 2), vec![layer(1, vec![]), LayerNode::Additional(vec![layer(0, vec![])])]);
124 }
125
126 #[test]
127 fn a_group_listed_twice_is_shown_twice() {
128 assert_eq!(layer_tree(&[Group(0), Array(vec![Text("Again".into()), Group(0)])], 1), vec![layer(0, vec![]), label("Again", vec![layer(0, vec![])])]);
129 }
130
131 #[test]
132 fn nesting_stops_at_pdf_js_limit() {
133 let mut deep = vec![Group(1)];
134 for _ in 0..30 {
135 deep = vec![Array(vec![Text("L".into())].into_iter().chain(deep).collect())];
136 }
137 let tree = layer_tree(&deep, 2);
138 let mut depth = 0;
139 let mut at = &tree;
140 while let Some(LayerNode::Label { children, .. }) = at.first() {
141 depth += 1;
142 at = children;
143 }
144 assert!(depth == 0, "past the limit nothing is kept, got {depth} levels");
145 assert!(tree.iter().any(|n| matches!(n, LayerNode::Additional(c) if c.contains(&layer(1, vec![])))));
147 let shallow = vec![Array(vec![Text("L".into()), Group(0)])];
148 assert_eq!(layer_tree(&shallow, 1), vec![label("L", vec![layer(0, vec![])])]);
149 }
150}