1use std::borrow::Cow;
4
5use indextree::{Arena, NodeId};
6
7use super::{Attr, ChangedGroup, FormatArena, FormattedValue};
8
9#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
11pub enum ElementChange {
12 #[default]
14 None,
15 Deleted,
17 Inserted,
19 MovedFrom,
21 MovedTo,
23}
24
25impl ElementChange {
26 pub fn prefix(self) -> Option<char> {
28 match self {
29 Self::None => None,
30 Self::Deleted => Some('-'),
31 Self::Inserted => Some('+'),
32 Self::MovedFrom => Some('←'),
33 Self::MovedTo => Some('→'),
34 }
35 }
36
37 pub fn has_prefix(self) -> bool {
39 !matches!(self, Self::None)
40 }
41}
42
43#[derive(Clone, Debug)]
45pub enum LayoutNode {
46 Element {
48 tag: Cow<'static, str>,
50 field_name: Option<&'static str>,
52 attrs: Vec<Attr>,
54 changed_groups: Vec<ChangedGroup>,
56 change: ElementChange,
58 },
59
60 Sequence {
62 change: ElementChange,
64 item_type: &'static str,
66 field_name: Option<&'static str>,
68 },
69
70 Collapsed {
72 count: usize,
74 },
75
76 Text {
78 value: FormattedValue,
80 change: ElementChange,
82 },
83
84 ItemGroup {
87 items: Vec<FormattedValue>,
89 change: ElementChange,
91 collapsed_suffix: Option<usize>,
94 item_type: &'static str,
96 },
97}
98
99impl LayoutNode {
100 pub fn element(tag: impl Into<Cow<'static, str>>) -> Self {
102 Self::Element {
103 tag: tag.into(),
104 field_name: None,
105 attrs: Vec::new(),
106 changed_groups: Vec::new(),
107 change: ElementChange::None,
108 }
109 }
110
111 pub fn element_with_change(tag: impl Into<Cow<'static, str>>, change: ElementChange) -> Self {
113 Self::Element {
114 tag: tag.into(),
115 field_name: None,
116 attrs: Vec::new(),
117 changed_groups: Vec::new(),
118 change,
119 }
120 }
121
122 pub fn sequence(change: ElementChange, item_type: &'static str) -> Self {
124 Self::Sequence {
125 change,
126 item_type,
127 field_name: None,
128 }
129 }
130
131 pub fn collapsed(count: usize) -> Self {
133 Self::Collapsed { count }
134 }
135
136 pub fn text(value: FormattedValue, change: ElementChange) -> Self {
138 Self::Text { value, change }
139 }
140
141 pub fn item_group(
143 items: Vec<FormattedValue>,
144 change: ElementChange,
145 collapsed_suffix: Option<usize>,
146 item_type: &'static str,
147 ) -> Self {
148 Self::ItemGroup {
149 items,
150 change,
151 collapsed_suffix,
152 item_type,
153 }
154 }
155
156 pub fn change(&self) -> ElementChange {
158 match self {
159 Self::Element { change, .. } => *change,
160 Self::Sequence { change, .. } => *change,
161 Self::Collapsed { .. } => ElementChange::None,
162 Self::Text { change, .. } => *change,
163 Self::ItemGroup { change, .. } => *change,
164 }
165 }
166
167 pub fn has_changes(&self) -> bool {
169 match self {
170 Self::Element {
171 attrs,
172 change,
173 changed_groups,
174 ..
175 } => {
176 change.has_prefix()
177 || !changed_groups.is_empty()
178 || attrs.iter().any(|a| {
179 matches!(
180 a.status,
181 super::AttrStatus::Changed { .. }
182 | super::AttrStatus::Deleted { .. }
183 | super::AttrStatus::Inserted { .. }
184 )
185 })
186 }
187 Self::Sequence { change, .. } => change.has_prefix(),
188 Self::Collapsed { .. } => false,
189 Self::Text { change, .. } => change.has_prefix(),
190 Self::ItemGroup { change, .. } => change.has_prefix(),
191 }
192 }
193}
194
195pub struct Layout {
197 pub strings: FormatArena,
199 pub tree: Arena<LayoutNode>,
201 pub root: NodeId,
203}
204
205impl Layout {
206 pub fn new(strings: FormatArena, mut tree: Arena<LayoutNode>, root_node: LayoutNode) -> Self {
208 let root = tree.new_node(root_node);
209 Self {
210 strings,
211 tree,
212 root,
213 }
214 }
215
216 pub fn get(&self, id: NodeId) -> Option<&LayoutNode> {
218 self.tree.get(id).map(|n| n.get())
219 }
220
221 pub fn get_mut(&mut self, id: NodeId) -> Option<&mut LayoutNode> {
223 self.tree.get_mut(id).map(|n| n.get_mut())
224 }
225
226 pub fn add_child(&mut self, parent: NodeId, child_node: LayoutNode) -> NodeId {
228 let child = self.tree.new_node(child_node);
229 parent.append(child, &mut self.tree);
230 child
231 }
232
233 pub fn children(&self, parent: NodeId) -> impl Iterator<Item = NodeId> + '_ {
235 parent.children(&self.tree)
236 }
237
238 pub fn get_string(&self, span: super::Span) -> &str {
240 self.strings.get(span)
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 #[test]
249 fn test_element_change_prefix() {
250 assert_eq!(ElementChange::None.prefix(), None);
251 assert_eq!(ElementChange::Deleted.prefix(), Some('-'));
252 assert_eq!(ElementChange::Inserted.prefix(), Some('+'));
253 assert_eq!(ElementChange::MovedFrom.prefix(), Some('←'));
254 assert_eq!(ElementChange::MovedTo.prefix(), Some('→'));
255 }
256
257 #[test]
258 fn test_layout_tree() {
259 let arena = FormatArena::new();
260 let tree = Arena::new();
261
262 let mut layout = Layout::new(arena, tree, LayoutNode::element("root"));
263
264 let child1 = layout.add_child(layout.root, LayoutNode::element("child1"));
265 let child2 = layout.add_child(layout.root, LayoutNode::element("child2"));
266
267 let children: Vec<_> = layout.children(layout.root).collect();
268 assert_eq!(children.len(), 2);
269 assert_eq!(children[0], child1);
270 assert_eq!(children[1], child2);
271 }
272
273 #[test]
274 fn test_collapsed_node() {
275 let node = LayoutNode::collapsed(5);
276 assert!(!node.has_changes());
277
278 if let LayoutNode::Collapsed { count } = node {
279 assert_eq!(count, 5);
280 } else {
281 panic!("expected Collapsed node");
282 }
283 }
284}