1use crate::{
2 Cursor, CursorSink, KindSet, Parse, Parser, Peek, Result as ParserResult, 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(p: &Parser<'a>, c: Cursor) -> bool {
56 T::peek(p, c)
57 }
58}
59
60impl<'a, T: Parse<'a> + Peek<'a>, const MIN: usize> Parse<'a> for CommaSeparated<'a, T, MIN> {
61 fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
62 let mut items = Self::new_in(p.bump());
63 if MIN == 0 && !p.peek::<T>() {
64 return Ok(items);
65 }
66 loop {
67 let item = p.parse::<T>()?;
68 let comma = p.parse_if_peek::<Comma>()?;
69 items.items.push((item, comma));
70 if comma.is_none() {
71 if MIN > items.len() {
72 p.parse::<Comma>()?;
73 }
74 return Ok(items);
75 }
76 }
77 }
78}
79
80impl<'a, T: ToCursors, const MIN: usize> ToCursors for CommaSeparated<'a, T, MIN> {
81 fn to_cursors(&self, s: &mut impl CursorSink) {
82 ToCursors::to_cursors(&self.items, s);
83 }
84}
85
86impl<'a, T: ToSpan, const MIN: usize> ToSpan for CommaSeparated<'a, T, MIN> {
87 fn to_span(&self) -> Span {
88 let first = self.items[0].to_span();
89 first + self.items.last().map(|t| t.to_span()).unwrap_or(first)
90 }
91}
92
93impl<'a, T, const MIN: usize> IntoIterator for CommaSeparated<'a, T, MIN> {
94 type Item = (T, Option<Comma>);
95 type IntoIter = IntoIter<'a, Self::Item>;
96
97 fn into_iter(self) -> Self::IntoIter {
98 self.items.into_iter()
99 }
100}
101
102impl<'a, 'b, T, const MIN: usize> IntoIterator for &'b CommaSeparated<'a, T, MIN> {
103 type Item = &'b (T, Option<Comma>);
104 type IntoIter = Iter<'b, (T, Option<Comma>)>;
105
106 fn into_iter(self) -> Self::IntoIter {
107 self.items.iter()
108 }
109}
110
111impl<'a, 'b, T, const MIN: usize> IntoIterator for &'b mut CommaSeparated<'a, T, MIN> {
112 type Item = &'b mut (T, Option<Comma>);
113 type IntoIter = IterMut<'b, (T, Option<Comma>)>;
114
115 fn into_iter(self) -> Self::IntoIter {
116 self.items.iter_mut()
117 }
118}
119
120impl<'a, T, I, const MIN: usize> Index<I> for CommaSeparated<'a, T, MIN>
121where
122 I: ::core::slice::SliceIndex<[(T, Option<Comma>)]>,
123{
124 type Output = I::Output;
125
126 #[inline]
127 fn index(&self, index: I) -> &Self::Output {
128 Index::index(&self.items, index)
129 }
130}
131
132impl<'a, T, I, const MIN: usize> IndexMut<I> for CommaSeparated<'a, T, MIN>
133where
134 I: ::core::slice::SliceIndex<[(T, Option<Comma>)]>,
135{
136 #[inline]
137 fn index_mut(&mut self, index: I) -> &mut Self::Output {
138 IndexMut::index_mut(&mut self.items, index)
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::{T, test_helpers::*};
146
147 #[test]
148 fn size_test() {
149 assert_eq!(std::mem::size_of::<CommaSeparated<T![Ident]>>(), 32);
150 }
151
152 #[test]
153 fn test_writes() {
154 assert_parse!(CommaSeparated<T![Ident]>, "foo");
155 assert_parse!(CommaSeparated<T![Ident]>, "one,two");
156 assert_parse!(CommaSeparated<T![Ident]>, "one,two,three");
157 assert_parse!(CommaSeparated<T![Ident], 0>, "");
158 }
159
160 #[test]
161 fn test_spans() {
162 assert_parse_span!(
163 CommaSeparated<T![Ident]>,
164 r#"
165 foo bar
166 ^^^
167 "#
168 );
169 assert_parse_span!(
170 CommaSeparated<T![Ident]>,
171 r#"
172 foo, bar, baz 1
173 ^^^^^^^^^^^^^
174 "#
175 );
176 }
177
178 #[test]
179 fn test_errors() {
180 assert_parse_error!(CommaSeparated<T![Ident]>, "");
181 assert_parse_error!(CommaSeparated<T![Ident]>, ",");
182 assert_parse_error!(CommaSeparated<T![Ident]>, "one,two,three,");
183 assert_parse_error!(CommaSeparated<T![Ident]>, "one two");
184 assert_parse_error!(CommaSeparated<T![Ident], 2>, "one");
185 assert_parse_error!(CommaSeparated<T![Ident], 3>, "one, two");
186 }
187}