ephemeris_documents/toml/
order.rs1use toml_edit::{Item, Table};
4
5use super::decor::prefix_str;
6
7struct Slot {
9 depth: usize,
10 old: Option<isize>,
11 prints_header: bool,
12 has_glued_comment: bool,
13 rank: usize,
14 blank_before: bool,
15}
16
17impl Slot {
18 fn of(table: &Table, depth: usize) -> Self {
19 Self {
20 depth,
21 old: table.position(),
22 prints_header: !table.is_implicit() || Self::has_values(table),
23 has_glued_comment: prefix_str(table.decor())
24 .is_some_and(|prefix| !prefix.is_empty() && !prefix.starts_with(['\n', '\r'])),
25 rank: 0,
26 blank_before: false,
27 }
28 }
29
30 fn has_values(table: &Table) -> bool {
31 table.iter().any(|(_, item)| match item {
32 Item::Table(child) => child.is_dotted(),
33 Item::ArrayOfTables(_) => false,
34 Item::Value(_) | Item::None => true,
35 })
36 }
37
38 const fn is_new(&self) -> bool {
39 self.old.is_none()
40 }
41}
42
43pub(super) struct TableOrder(Vec<Slot>);
49
50impl TableOrder {
51 pub(super) fn of(root: &mut Table) -> Self {
53 let mut slots = Vec::new();
54 let root_has_values = Slot::has_values(root);
55 Self::walk(root, 0, &mut |table, depth| {
56 slots.push(Slot::of(table, depth));
57 });
58 Self::place_root_first(&mut slots, root_has_values);
59 let order = Self::ordered(&slots);
60 for (rank, &index) in order.iter().enumerate() {
61 let blank_before = Self::needs_blank(&slots, &order, rank);
62 if let Some(slot) = slots.get_mut(index) {
63 slot.rank = rank;
64 slot.blank_before = blank_before;
65 }
66 }
67 Self(slots)
68 }
69
70 const fn place_root_first(slots: &mut [Slot], has_values: bool) {
72 if let Some(root) = slots.first_mut() {
73 root.old = None;
74 root.prints_header = has_values;
75 }
76 }
77
78 fn ordered(slots: &[Slot]) -> Vec<usize> {
81 let mut order: Vec<usize> = (0..slots.len())
82 .filter(|&index| slots.get(index).is_some_and(|slot| !slot.is_new()))
83 .collect();
84 order.sort_by_key(|&index| (slots.get(index).and_then(|slot| slot.old), index));
85 for (index, slot) in slots.iter().enumerate() {
86 if slot.is_new() {
87 let after = Self::slot_after_subtree(slots, &order, index);
88 order.insert(after, index);
89 }
90 }
91 order
92 }
93
94 fn slot_after_subtree(slots: &[Slot], order: &[usize], index: usize) -> usize {
98 let own = index.saturating_add(1)..Self::subtree_end(slots, index);
99 if let Some(at) = order.iter().position(|other| own.contains(other)) {
100 return at;
101 }
102 let depth = slots.get(index).map_or(0, |slot| slot.depth);
103 let Some(parent) = slots
104 .get(..index)
105 .and_then(|before| before.iter().rposition(|slot| slot.depth < depth))
106 else {
107 return 0;
108 };
109 let family = parent..Self::subtree_end(slots, parent);
110 order
111 .iter()
112 .rposition(|other| family.contains(other))
113 .map_or(0, |at| at.saturating_add(1))
114 }
115
116 fn subtree_end(slots: &[Slot], index: usize) -> usize {
118 let depth = slots.get(index).map_or(0, |slot| slot.depth);
119 slots
120 .iter()
121 .enumerate()
122 .skip(index.saturating_add(1))
123 .find(|(_, slot)| slot.depth <= depth)
124 .map_or(slots.len(), |(at, _)| at)
125 }
126
127 fn needs_blank(slots: &[Slot], order: &[usize], rank: usize) -> bool {
133 let slot = order.get(rank).and_then(|&index| slots.get(index));
134 let is_existing = slot.is_some_and(|slot| !slot.is_new());
135 let is_commented_header = rank > 0
136 && slot
137 .is_some_and(|slot| slot.is_new() && slot.prints_header && slot.has_glued_comment)
138 && Self::prints_before(slots, order, rank);
139 let follows_new_header = rank
140 .checked_sub(1)
141 .and_then(|before| order.get(before))
142 .is_some_and(|&before| {
143 before != 0
144 && slots
145 .get(before)
146 .is_some_and(|previous| previous.is_new() && previous.prints_header)
147 });
148 (is_existing && follows_new_header) || is_commented_header
149 }
150
151 fn prints_before(slots: &[Slot], order: &[usize], rank: usize) -> bool {
153 order
154 .iter()
155 .take(rank)
156 .any(|&index| slots.get(index).is_some_and(|slot| slot.prints_header))
157 }
158
159 pub(super) fn apply(&self, root: &mut Table) {
163 let mut index = 0usize;
164 Self::walk(root, 0, &mut |table, _| {
165 let slot = self.0.get(index);
166 let rank = slot.map_or(0, |slot| slot.rank);
167 table.set_position(isize::try_from(rank).ok());
168 if slot.is_some_and(|slot| slot.blank_before) {
169 let prefix = prefix_str(table.decor())
170 .filter(|prefix| !prefix.starts_with(['\n', '\r']))
171 .map(|prefix| format!("\n{prefix}"));
172 if let Some(prefix) = prefix {
173 table.decor_mut().set_prefix(prefix);
174 }
175 }
176 index = index.saturating_add(1);
177 });
178 }
179
180 pub(super) fn forget(item: &mut Item) {
183 match item {
184 Item::Table(table) => {
185 table.set_position(None);
186 for (_, child) in table.iter_mut() {
187 Self::forget(child);
188 }
189 }
190 Item::ArrayOfTables(tables) => {
191 for table in tables.iter_mut() {
192 table.set_position(None);
193 for (_, child) in table.iter_mut() {
194 Self::forget(child);
195 }
196 }
197 }
198 Item::None | Item::Value(_) => {}
199 }
200 }
201
202 fn walk(table: &mut Table, depth: usize, visit: &mut impl FnMut(&mut Table, usize)) {
205 let depth = if table.is_dotted() {
206 depth
207 } else {
208 visit(table, depth);
209 depth.saturating_add(1)
210 };
211 for (_, item) in table.iter_mut() {
212 match item {
213 Item::Table(child) => Self::walk(child, depth, visit),
214 Item::ArrayOfTables(children) => {
215 for child in children.iter_mut() {
216 Self::walk(child, depth, visit);
217 }
218 }
219 Item::None | Item::Value(_) => {}
220 }
221 }
222 }
223}