Skip to main content

toml_spanner/item/
to_toml.rs

1use super::array::Array;
2use super::table::Table;
3use super::{FLAG_MASK, HINTS_BIT, Item, ItemMetadata, NOT_PROJECTED, TAG_MASK, TAG_SHIFT};
4
5/// Bit 30 of `end_and_flag`: marks a full match during reprojection.
6pub(crate) const FULL_MATCH_BIT: u32 = 1 << 30;
7/// Bit 29 of `end_and_flag`: when set in format-hints mode, disables
8/// source-position reordering for this table's immediate entries.
9pub(crate) const IGNORE_SOURCE_ORDER_BIT: u32 = 1 << 29;
10/// Bit 28 of `end_and_flag`: when set in format-hints mode, disables
11/// copying structural styles from source during reprojection.
12pub(crate) const IGNORE_SOURCE_STYLE_BIT: u32 = 1 << 28;
13/// Bit 27 of `end_and_flag`: marks an array element as reordered during
14/// reprojection. Prevents the emitter from sorting this element back to
15/// its original source position in the parent array, without affecting
16/// source-ordering of the element's own children.
17pub(crate) const ARRAY_REORDERED_BIT: u32 = 1 << 27;
18/// Bit 25 of `end_and_flag`: when set in format-hints mode, indicates that
19/// an inline array should be emitted in multiline format with one element
20/// per line and trailing commas.
21pub(crate) const EXPANDED_BIT: u32 = 1 << 25;
22/// Bit 24 of `end_and_flag`: when set in format-hints mode, prevents this
23/// item from being reprojected during format-preserving emission.
24pub(crate) const IGNORE_SOURCE_FORMATTING_RECURSIVELY_BIT: u32 = 1 << 24;
25/// Bit 23 of `end_and_flag`: when set in format-hints mode, `start_and_tag`
26/// still stores the source span start rather than a projected source index.
27pub(crate) const PRESERVED_SPAN_START_BIT: u32 = 1 << 23;
28
29impl ItemMetadata {
30    /// Returns the projected index (bits 3-31 of `start_and_tag`).
31    #[inline]
32    pub(crate) fn projected_index(&self) -> u32 {
33        self.start_and_tag >> TAG_SHIFT
34    }
35
36    /// Branchless mask: span mode -> FLAG_MASK (clears stale span data),
37    /// hints mode -> 0xFFFFFFFF (preserves existing hint bits).
38    #[inline]
39    fn hints_preserve_mask(&self) -> u32 {
40        ((self.end_and_flag as i32) >> 31) as u32 | FLAG_MASK
41    }
42
43    #[inline]
44    fn enter_hints_preserving_span_start(&mut self) {
45        let was_span_mode = self.is_span_mode();
46        self.end_and_flag |= HINTS_BIT;
47        if was_span_mode {
48            self.end_and_flag |= PRESERVED_SPAN_START_BIT;
49        }
50    }
51
52    #[inline]
53    pub(crate) fn span_identity_start(&self) -> Option<u32> {
54        let has_preserved_start = self.end_and_flag & (HINTS_BIT | PRESERVED_SPAN_START_BIT)
55            == (HINTS_BIT | PRESERVED_SPAN_START_BIT);
56        if self.is_span_mode() || has_preserved_start {
57            Some(self.start_and_tag >> TAG_SHIFT)
58        } else {
59            None
60        }
61    }
62
63    /// Stores a reprojected index, preserving user-set hint bits when
64    /// already in hints mode. Returns `false` if the index doesn't fit.
65    #[inline]
66    pub(crate) fn set_reprojected_index(&mut self, index: usize) -> bool {
67        if index <= (u32::MAX >> TAG_SHIFT) as usize {
68            self.start_and_tag = (self.start_and_tag & TAG_MASK) | ((index as u32) << TAG_SHIFT);
69            self.end_and_flag =
70                (self.end_and_flag & (self.hints_preserve_mask() & !PRESERVED_SPAN_START_BIT))
71                    | HINTS_BIT;
72            true
73        } else {
74            false
75        }
76    }
77
78    /// Marks as not projected, preserving user-set hint bits when already
79    /// in hints mode and clearing full-match.
80    #[inline]
81    pub(crate) fn set_reprojected_to_none(&mut self) {
82        self.start_and_tag |= NOT_PROJECTED;
83        self.end_and_flag =
84            (self.end_and_flag
85                & (self.hints_preserve_mask()
86                    & !(FULL_MATCH_BIT | PRESERVED_SPAN_START_BIT)))
87                | HINTS_BIT;
88    }
89
90    #[inline]
91    pub(crate) fn set_reprojected_full_match(&mut self) {
92        self.end_and_flag |= FULL_MATCH_BIT;
93    }
94
95    #[inline]
96    pub(crate) fn is_reprojected_full_match(&self) -> bool {
97        self.end_and_flag & FULL_MATCH_BIT != 0
98    }
99
100    /// Disables source-position reordering for this table's entries.
101    #[inline]
102    pub(crate) fn set_ignore_source_order(&mut self) {
103        self.enter_hints_preserving_span_start();
104        self.end_and_flag |= IGNORE_SOURCE_ORDER_BIT;
105    }
106
107    /// Returns `true` if source-position reordering is disabled.
108    /// Gates on `HINTS_BIT` so stale span-end bits cannot false-positive.
109    #[inline]
110    pub(crate) fn ignore_source_order(&self) -> bool {
111        self.end_and_flag & (HINTS_BIT | IGNORE_SOURCE_ORDER_BIT)
112            == (HINTS_BIT | IGNORE_SOURCE_ORDER_BIT)
113    }
114
115    /// Marks an array element as reordered during reprojection.
116    #[inline]
117    pub(crate) fn set_array_reordered(&mut self) {
118        self.enter_hints_preserving_span_start();
119        self.end_and_flag |= ARRAY_REORDERED_BIT;
120    }
121
122    /// Returns `true` if this element was reordered during array reprojection.
123    #[inline]
124    pub(crate) fn array_reordered(&self) -> bool {
125        self.end_and_flag & (HINTS_BIT | ARRAY_REORDERED_BIT) == (HINTS_BIT | ARRAY_REORDERED_BIT)
126    }
127
128    /// Disables copying structural styles from source during reprojection.
129    #[inline]
130    pub(crate) fn set_ignore_source_style(&mut self) {
131        self.enter_hints_preserving_span_start();
132        self.end_and_flag |= IGNORE_SOURCE_STYLE_BIT;
133    }
134
135    /// Returns `true` if source-style copying is disabled for this table.
136    #[inline]
137    pub(crate) fn ignore_source_style(&self) -> bool {
138        self.end_and_flag & (HINTS_BIT | IGNORE_SOURCE_STYLE_BIT)
139            == (HINTS_BIT | IGNORE_SOURCE_STYLE_BIT)
140    }
141
142    #[inline]
143    pub(crate) fn set_expanded(&mut self) {
144        self.enter_hints_preserving_span_start();
145        self.end_and_flag |= EXPANDED_BIT;
146    }
147
148    #[inline]
149    pub(crate) fn is_expanded(&self) -> bool {
150        self.end_and_flag & (HINTS_BIT | EXPANDED_BIT) == (HINTS_BIT | EXPANDED_BIT)
151    }
152
153    #[inline]
154    pub(crate) fn clear_expanded(&mut self) {
155        self.end_and_flag &= !EXPANDED_BIT;
156    }
157
158    /// Prevents this item from being reprojected during format-preserving emission.
159    #[inline]
160    pub(crate) fn set_ignore_source_formatting_recursively(&mut self) {
161        self.enter_hints_preserving_span_start();
162        self.end_and_flag |= IGNORE_SOURCE_FORMATTING_RECURSIVELY_BIT;
163    }
164
165    /// Returns `true` if this item should skip reprojection and use formatted output.
166    #[inline]
167    pub(crate) fn ignore_source_formatting_recursively(&self) -> bool {
168        self.end_and_flag & (HINTS_BIT | IGNORE_SOURCE_FORMATTING_RECURSIVELY_BIT)
169            == (HINTS_BIT | IGNORE_SOURCE_FORMATTING_RECURSIVELY_BIT)
170    }
171}
172
173impl<'de> Item<'de> {
174    /// Access projected item from source computed in reprojection.
175    pub(crate) fn projected<'a>(&self, inputs: &[&'a Item<'a>]) -> Option<&'a Item<'a>> {
176        let index = self.meta.projected_index();
177        inputs.get(index as usize).copied()
178    }
179    pub(crate) fn set_reprojected_to_none(&mut self) {
180        self.meta.set_reprojected_to_none();
181    }
182    pub(crate) fn set_reprojected_index(&mut self, index: usize) -> bool {
183        self.meta.set_reprojected_index(index)
184    }
185    /// Marks this item as a full match during reprojection.
186    pub(crate) fn set_reprojected_full_match(&mut self) {
187        self.meta.set_reprojected_full_match();
188    }
189    /// Returns whether this item was marked as a full match during reprojection.
190    pub(crate) fn is_reprojected_full_match(&self) -> bool {
191        self.meta.is_reprojected_full_match()
192    }
193
194    /// Prevents this item and its entire subtree from using source formatting
195    /// during format-preserving emission. The item will be emitted with clean
196    /// formatted output even when the rest of the document preserves source
197    /// formatting via reprojection.
198    pub fn set_ignore_source_formatting_recursively(&mut self) {
199        self.meta.set_ignore_source_formatting_recursively();
200    }
201
202    /// Returns `true` if this item will skip source formatting during emission.
203    #[must_use]
204    pub fn ignore_source_formatting_recursively(&self) -> bool {
205        self.meta.ignore_source_formatting_recursively()
206    }
207
208    /// Returns `true` if this item is emitted as a subsection rather than
209    /// as part of the body: `[header]` tables, implicit tables, and
210    /// `[[array-of-tables]]`.
211    #[inline]
212    pub(crate) fn is_subsection(&self) -> bool {
213        self.has_header_bit() || self.is_implicit_table() || self.is_aot()
214    }
215
216    #[inline]
217    #[cfg(test)]
218    pub(crate) fn set_flag(&mut self, flag: u32) {
219        self.meta.set_flag(flag);
220    }
221}
222
223impl<'de> Table<'de> {
224    /// Disables source-position reordering for this table's immediate entries
225    /// during emission. Non-recursive: child tables are unaffected.
226    pub fn set_ignore_source_order(&mut self) {
227        self.meta.set_ignore_source_order();
228    }
229
230    /// Returns `true` if source-position reordering is disabled for this table.
231    #[must_use]
232    pub fn ignore_source_order(&self) -> bool {
233        self.meta.ignore_source_order()
234    }
235
236    /// Disables copying structural styles (TableStyle/ArrayStyle) from source
237    /// during reprojection for this table's immediate entries. Key spans and
238    /// reprojection indices are still copied. Non-recursive.
239    pub fn set_ignore_source_style(&mut self) {
240        self.meta.set_ignore_source_style();
241    }
242
243    /// Returns `true` if source-style copying is disabled for this table.
244    #[must_use]
245    pub fn ignore_source_style(&self) -> bool {
246        self.meta.ignore_source_style()
247    }
248
249    /// Returns `true` if this table has automatic style resolution pending.
250    #[must_use]
251    pub fn is_auto_style(&self) -> bool {
252        self.meta.is_auto_style()
253    }
254}
255
256impl<'de> Array<'de> {
257    /// Returns `true` if this array has automatic style resolution pending.
258    #[must_use]
259    pub fn is_auto_style(&self) -> bool {
260        self.meta.is_auto_style()
261    }
262
263    /// Returns `true` if this inline array should be emitted in multiline
264    /// format with one element per line.
265    #[must_use]
266    pub fn is_expanded(&self) -> bool {
267        self.meta.is_expanded()
268    }
269
270    /// Marks this inline array for multiline emission.
271    pub fn set_expanded(&mut self) {
272        self.meta.set_expanded();
273    }
274
275    /// Clears the multiline emission hint.
276    pub fn clear_expanded(&mut self) {
277        self.meta.clear_expanded();
278    }
279}