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