1use crate::{
2 Cursor, CursorSink, KindSet, Parse, Parser, Peek, Result as ParserResult, SemanticEq, Span, ToCursors, ToSpan,
3 token_macros::Comma,
4};
5use bumpalo::{
6 Bump,
7 collections::{Vec, vec::IntoIter},
8};
9use std::{
10 ops::{Index, IndexMut},
11 slice::{Iter, IterMut},
12};
13
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
35pub struct CommaSeparated<'a, T, const MIN: usize = 1> {
36 items: Vec<'a, (T, Option<Comma>)>,
37}
38
39impl<'a, T, const MIN: usize> CommaSeparated<'a, T, MIN> {
40 pub fn new_in(bump: &'a Bump) -> Self {
41 Self { items: Vec::new_in(bump) }
42 }
43
44 pub fn is_empty(&self) -> bool {
45 self.items.is_empty()
46 }
47
48 pub fn len(&self) -> usize {
49 self.items.len()
50 }
51}
52
53impl<'a, T: Peek<'a>, const MIN: usize> Peek<'a> for CommaSeparated<'a, T, MIN> {
54 const PEEK_KINDSET: KindSet = T::PEEK_KINDSET;
55 fn peek<Iter>(p: &Parser<'a, Iter>, c: Cursor) -> bool
56 where
57 Iter: Iterator<Item = crate::Cursor> + Clone,
58 {
59 T::peek(p, c)
60 }
61}
62
63impl<'a, T: Parse<'a> + Peek<'a>, const MIN: usize> Parse<'a> for CommaSeparated<'a, T, MIN> {
64 fn parse<Iter>(p: &mut Parser<'a, Iter>) -> ParserResult<Self>
65 where
66 Iter: Iterator<Item = crate::Cursor> + Clone,
67 {
68 let mut items = Self::new_in(p.bump());
69 if MIN == 0 && !<T>::peek(p, p.peek_n(1)) {
70 return Ok(items);
71 }
72 loop {
73 let item = p.parse::<T>()?;
74 let comma = p.parse_if_peek::<Comma>()?;
75 items.items.push((item, comma));
76 if comma.is_none() {
77 if MIN > items.len() {
78 p.parse::<Comma>()?;
79 }
80 return Ok(items);
81 }
82 }
83 }
84}
85
86impl<'a, T: ToCursors, const MIN: usize> ToCursors for CommaSeparated<'a, T, MIN> {
87 fn to_cursors(&self, s: &mut impl CursorSink) {
88 ToCursors::to_cursors(&self.items, s);
89 }
90}
91
92impl<'a, T: ToSpan, const MIN: usize> ToSpan for CommaSeparated<'a, T, MIN> {
93 fn to_span(&self) -> Span {
94 let first = self.items[0].to_span();
95 first + self.items.last().map(|t| t.to_span()).unwrap_or(first)
96 }
97}
98
99impl<'a, T: SemanticEq, const MIN: usize> SemanticEq for CommaSeparated<'a, T, MIN> {
100 fn semantic_eq(&self, other: &Self) -> bool {
101 self.items.semantic_eq(&other.items)
102 }
103}
104
105impl<'a, T, const MIN: usize> IntoIterator for CommaSeparated<'a, T, MIN> {
106 type Item = (T, Option<Comma>);
107 type IntoIter = IntoIter<'a, Self::Item>;
108
109 fn into_iter(self) -> Self::IntoIter {
110 self.items.into_iter()
111 }
112}
113
114impl<'a, 'b, T, const MIN: usize> IntoIterator for &'b CommaSeparated<'a, T, MIN> {
115 type Item = &'b (T, Option<Comma>);
116 type IntoIter = Iter<'b, (T, Option<Comma>)>;
117
118 fn into_iter(self) -> Self::IntoIter {
119 self.items.iter()
120 }
121}
122
123impl<'a, 'b, T, const MIN: usize> IntoIterator for &'b mut CommaSeparated<'a, T, MIN> {
124 type Item = &'b mut (T, Option<Comma>);
125 type IntoIter = IterMut<'b, (T, Option<Comma>)>;
126
127 fn into_iter(self) -> Self::IntoIter {
128 self.items.iter_mut()
129 }
130}
131
132impl<'a, T, I, const MIN: usize> Index<I> for CommaSeparated<'a, T, MIN>
133where
134 I: ::core::slice::SliceIndex<[(T, Option<Comma>)]>,
135{
136 type Output = I::Output;
137
138 #[inline]
139 fn index(&self, index: I) -> &Self::Output {
140 Index::index(&self.items, index)
141 }
142}
143
144impl<'a, T, I, const MIN: usize> IndexMut<I> for CommaSeparated<'a, T, MIN>
145where
146 I: ::core::slice::SliceIndex<[(T, Option<Comma>)]>,
147{
148 #[inline]
149 fn index_mut(&mut self, index: I) -> &mut Self::Output {
150 IndexMut::index_mut(&mut self.items, index)
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::{EmptyAtomSet, T, test_helpers::*};
158
159 #[test]
160 fn size_test() {
161 assert_eq!(std::mem::size_of::<CommaSeparated<T![Ident]>>(), 32);
162 }
163
164 #[test]
165 fn test_writes() {
166 assert_parse!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "foo");
167 assert_parse!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "one,two");
168 assert_parse!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "one,two,three");
169 assert_parse!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident], 0>, "");
170 }
171
172 #[test]
173 fn test_spans() {
174 assert_parse_span!(
175 EmptyAtomSet::ATOMS,
176 CommaSeparated<T![Ident]>,
177 r#"
178 foo bar
179 ^^^
180 "#
181 );
182 assert_parse_span!(
183 EmptyAtomSet::ATOMS,
184 CommaSeparated<T![Ident]>,
185 r#"
186 foo, bar, baz 1
187 ^^^^^^^^^^^^^
188 "#
189 );
190 }
191
192 #[test]
193 fn test_errors() {
194 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "");
195 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, ",");
196 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "one,two,three,");
197 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "one two");
198 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident], 2>, "one");
199 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident], 3>, "one, two");
200 }
201}