synkit_core/
punctuated.rs

1/// Policy for trailing punctuation in punctuated sequences.
2///
3/// Controls whether a trailing separator (e.g., comma) is allowed after the last element.
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum TrailingPolicy {
7    /// Trailing punctuation is allowed but not required (e.g., `[1, 2, 3]` or `[1, 2, 3,]`).
8    Optional,
9    /// Trailing punctuation is required (e.g., `use foo;` where `;` is required).
10    Required,
11    /// Trailing punctuation is forbidden (e.g., function arguments: `f(a, b, c)`).
12    Forbidden,
13}
14
15/// Internal storage for punctuated sequences.
16///
17/// Stores pairs of values and optional punctuation. This is the shared
18/// implementation used by [`Punctuated`], [`Terminated`], and [`Separated`].
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[derive(Debug, Clone)]
21pub struct PunctuatedInner<T, P> {
22    pub(crate) inner: Vec<(T, Option<P>)>,
23}
24
25impl<T, P> PunctuatedInner<T, P> {
26    #[inline]
27    pub fn new() -> Self {
28        Self { inner: Vec::new() }
29    }
30
31    #[inline]
32    pub fn with_capacity(capacity: usize) -> Self {
33        Self {
34            inner: Vec::with_capacity(capacity),
35        }
36    }
37
38    #[inline]
39    pub fn push_value(&mut self, value: T) {
40        self.inner.push((value, None));
41    }
42
43    #[inline]
44    pub fn push_punct(&mut self, punct: P) {
45        if let Some(last) = self.inner.last_mut() {
46            last.1 = Some(punct);
47        }
48    }
49
50    #[inline]
51    pub fn trailing_punct(&self) -> bool {
52        self.inner.last().is_some_and(|(_, p)| p.is_some())
53    }
54
55    #[inline]
56    pub fn len(&self) -> usize {
57        self.inner.len()
58    }
59
60    #[inline]
61    pub fn is_empty(&self) -> bool {
62        self.inner.is_empty()
63    }
64
65    #[inline]
66    pub fn capacity(&self) -> usize {
67        self.inner.capacity()
68    }
69
70    #[inline]
71    pub fn reserve(&mut self, additional: usize) {
72        self.inner.reserve(additional);
73    }
74
75    #[inline]
76    pub fn shrink_to_fit(&mut self) {
77        self.inner.shrink_to_fit();
78    }
79
80    #[inline]
81    pub fn clear(&mut self) {
82        self.inner.clear();
83    }
84
85    #[inline]
86    pub fn iter(&self) -> impl Iterator<Item = &T> {
87        self.inner.iter().map(|(v, _)| v)
88    }
89
90    #[inline]
91    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
92        self.inner.iter_mut().map(|(v, _)| v)
93    }
94
95    #[inline]
96    pub fn pairs(&self) -> impl Iterator<Item = (&T, Option<&P>)> {
97        self.inner.iter().map(|(v, p)| (v, p.as_ref()))
98    }
99
100    #[inline]
101    pub fn pairs_mut(&mut self) -> impl Iterator<Item = (&mut T, Option<&mut P>)> {
102        self.inner.iter_mut().map(|(v, p)| (v, p.as_mut()))
103    }
104
105    #[inline]
106    pub fn into_pairs(self) -> impl Iterator<Item = (T, Option<P>)> {
107        self.inner.into_iter()
108    }
109
110    #[inline]
111    pub fn first(&self) -> Option<&T> {
112        self.inner.first().map(|(v, _)| v)
113    }
114
115    #[inline]
116    pub fn last(&self) -> Option<&T> {
117        self.inner.last().map(|(v, _)| v)
118    }
119}
120
121impl<T, P> Default for PunctuatedInner<T, P> {
122    fn default() -> Self {
123        Self::new()
124    }
125}
126
127impl<T, P> FromIterator<T> for PunctuatedInner<T, P> {
128    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
129        let inner: Vec<(T, Option<P>)> = iter.into_iter().map(|v| (v, None)).collect();
130        Self { inner }
131    }
132}
133
134/// Internal macro to generate punctuated wrapper types.
135///
136/// Each wrapper shares the same structure but differs in:
137/// - `POLICY` constant
138/// - `trailing_punct()` behavior
139/// - Documentation
140macro_rules! impl_punctuated_wrapper {
141    (
142        $(#[$attr:meta])*
143        $name:ident,
144        $policy:expr,
145        $trailing_punct:expr
146    ) => {
147        $(#[$attr])*
148        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
149        #[derive(Debug, Clone)]
150        pub struct $name<T, P>(PunctuatedInner<T, P>);
151
152        impl<T, P> $name<T, P> {
153            /// The trailing punctuation policy for this type.
154            pub const POLICY: TrailingPolicy = $policy;
155
156            /// Creates a new empty sequence.
157            #[inline]
158            pub fn new() -> Self {
159                Self(PunctuatedInner::new())
160            }
161
162            /// Creates a new sequence with pre-allocated capacity.
163            #[inline]
164            pub fn with_capacity(capacity: usize) -> Self {
165                Self(PunctuatedInner::with_capacity(capacity))
166            }
167
168            /// Pushes a value onto the sequence.
169            #[inline]
170            pub fn push_value(&mut self, value: T) {
171                self.0.push_value(value);
172            }
173
174            /// Attaches punctuation to the last value.
175            #[inline]
176            pub fn push_punct(&mut self, punct: P) {
177                self.0.push_punct(punct);
178            }
179
180            /// Returns whether the sequence has trailing punctuation.
181            ///
182            /// The return value depends on the wrapper type's policy:
183            /// - `Punctuated`: delegates to inner storage
184            /// - `Terminated`: always `true`
185            /// - `Separated`: always `false`
186            #[inline]
187            pub fn trailing_punct(&self) -> bool {
188                $trailing_punct(&self.0)
189            }
190
191            /// Consumes the wrapper and returns the inner storage.
192            #[inline]
193            pub fn into_inner(self) -> PunctuatedInner<T, P> {
194                self.0
195            }
196        }
197
198        impl<T, P> Default for $name<T, P> {
199            fn default() -> Self {
200                Self::new()
201            }
202        }
203
204        impl<T, P> IntoIterator for $name<T, P> {
205            type Item = T;
206            type IntoIter = std::iter::Map<std::vec::IntoIter<(T, Option<P>)>, fn((T, Option<P>)) -> T>;
207
208            fn into_iter(self) -> Self::IntoIter {
209                self.0.inner.into_iter().map(|(v, _)| v)
210            }
211        }
212
213        impl<T, P> FromIterator<T> for $name<T, P> {
214            fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
215                Self(PunctuatedInner::from_iter(iter))
216            }
217        }
218
219        impl<T, P> std::ops::Deref for $name<T, P> {
220            type Target = PunctuatedInner<T, P>;
221
222            #[inline]
223            fn deref(&self) -> &Self::Target {
224                &self.0
225            }
226        }
227
228        impl<T, P> std::ops::DerefMut for $name<T, P> {
229            #[inline]
230            fn deref_mut(&mut self) -> &mut Self::Target {
231                &mut self.0
232            }
233        }
234
235        impl<T, P> AsRef<PunctuatedInner<T, P>> for $name<T, P> {
236            #[inline]
237            fn as_ref(&self) -> &PunctuatedInner<T, P> {
238                &self.0
239            }
240        }
241
242        impl<T, P> AsMut<PunctuatedInner<T, P>> for $name<T, P> {
243            #[inline]
244            fn as_mut(&mut self) -> &mut PunctuatedInner<T, P> {
245                &mut self.0
246            }
247        }
248    };
249}
250
251impl_punctuated_wrapper!(
252    /// A punctuated sequence with optional trailing separator.
253    ///
254    /// Use this for lists where trailing punctuation is allowed but not required,
255    /// such as array literals: `[1, 2, 3]` or `[1, 2, 3,]`.
256    ///
257    /// # Type Parameters
258    ///
259    /// - `T`: The value type (e.g., expression)
260    /// - `P`: The punctuation/separator type (e.g., comma token)
261    ///
262    /// # Example
263    ///
264    /// ```ignore
265    /// // Parse: item, item, item
266    /// let mut items = Punctuated::<Item, CommaToken>::new();
267    /// items.push_value(stream.parse()?);
268    /// while stream.peek::<CommaToken>() {
269    ///     items.push_punct(stream.parse()?);
270    ///     if !stream.peek::<Item>() { break; } // Allow trailing comma
271    ///     items.push_value(stream.parse()?);
272    /// }
273    /// ```
274    Punctuated,
275    TrailingPolicy::Optional,
276    |inner: &PunctuatedInner<T, P>| inner.trailing_punct()
277);
278
279impl_punctuated_wrapper!(
280    /// A punctuated sequence with required trailing separator.
281    ///
282    /// Use this for statement-like constructs where each item must end with
283    /// punctuation, such as `use` statements: `use foo; use bar;`.
284    ///
285    /// # Type Parameters
286    ///
287    /// - `T`: The value type (e.g., statement)
288    /// - `P`: The punctuation/separator type (e.g., semicolon token)
289    Terminated,
290    TrailingPolicy::Required,
291    |_: &PunctuatedInner<T, P>| true
292);
293
294impl_punctuated_wrapper!(
295    /// A punctuated sequence where trailing separator is forbidden.
296    ///
297    /// Use this for function arguments or similar constructs where trailing
298    /// punctuation is invalid: `f(a, b, c)` not `f(a, b, c,)`.
299    ///
300    /// # Type Parameters
301    ///
302    /// - `T`: The value type (e.g., argument)
303    /// - `P`: The punctuation/separator type (e.g., comma token)
304    Separated,
305    TrailingPolicy::Forbidden,
306    |_: &PunctuatedInner<T, P>| false
307);