bexpand/
lib.rs

1use std::fmt::Debug;
2use std::str::FromStr;
3use std::{borrow::Cow, char::CharTryFromError, iter};
4
5use itertools::{Itertools, MultiProduct};
6use nom_language::error::{convert_error, VerboseError};
7
8mod parser;
9mod sequence;
10
11/// {a,b,c}
12#[derive(Clone, Debug)]
13struct List<'a>(Vec<Part<'a>>);
14
15impl<'a> List<'a> {
16    fn into_owned(self) -> List<'static> {
17        List(self.0.into_iter().map(Part::into_owned).collect())
18    }
19}
20
21impl<'a> IntoIterator for List<'a> {
22    type Item = Result<Cow<'a, str>, CharTryFromError>;
23
24    type IntoIter = iter::Flatten<<Vec<Part<'a>> as IntoIterator>::IntoIter>;
25
26    fn into_iter(self) -> Self::IntoIter {
27        self.0.into_iter().flatten()
28    }
29}
30
31impl std::fmt::Display for List<'_> {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        f.write_str("{")?;
34        let mut parts = self.0.iter();
35        if let Some(part) = parts.next() {
36            write!(f, "{part}")?;
37        }
38        for part in parts {
39            write!(f, ",{part}")?;
40        }
41        f.write_str("}")?;
42        Ok(())
43    }
44}
45
46#[derive(Clone, Copy, Debug)]
47enum Sequence {
48    Int {
49        width: Option<usize>,
50        sequence: sequence::Sequence<i64>,
51    },
52    Char(sequence::Sequence<char>),
53}
54
55#[derive(Clone, Copy, Debug)]
56enum SequenceIterator {
57    Int {
58        width: Option<usize>,
59        sequence: sequence::SequenceIterator<i64>,
60    },
61    Char(sequence::SequenceIterator<char>),
62}
63
64impl IntoIterator for Sequence {
65    type Item = Result<String, CharTryFromError>;
66
67    type IntoIter = SequenceIterator;
68
69    fn into_iter(self) -> Self::IntoIter {
70        match self {
71            Sequence::Int { width, sequence } => SequenceIterator::Int {
72                width,
73                sequence: sequence.into_iter(),
74            },
75            Sequence::Char(s) => SequenceIterator::Char(s.into_iter()),
76        }
77    }
78}
79
80impl Iterator for SequenceIterator {
81    type Item = Result<String, <u32 as TryInto<char>>::Error>;
82
83    fn next(&mut self) -> Option<Self::Item> {
84        match self {
85            SequenceIterator::Int { width, sequence } => {
86                sequence.next().map(|number| match *width {
87                    Some(width) => Ok(format!(
88                        "{number:0width$}",
89                        number = number.unwrap(),
90                        width = width,
91                    )),
92                    None => Ok(number.unwrap().to_string()),
93                })
94            }
95            SequenceIterator::Char(i) => i.next().map(|r| r.map(|c| c.to_string())),
96        }
97    }
98}
99
100impl std::fmt::Display for Sequence {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        f.write_str("{")?;
103        match *self {
104            Self::Int {
105                width,
106                sequence: sequence::Sequence { start, end, incr },
107            } => {
108                if width.is_some() {
109                    f.write_str("=")?;
110                }
111                write!(f, "{start}..{end}")?;
112                if incr != 1 {
113                    write!(f, "..{incr}")?;
114                }
115            }
116            Self::Char(sequence::Sequence { start, end, incr }) => {
117                let escaped = ",.{}\\";
118                if escaped.contains(start) {
119                    f.write_str("\\")?;
120                }
121                write!(f, "{start}..")?;
122                if escaped.contains(end) {
123                    f.write_str("\\")?;
124                }
125                write!(f, "{end}")?;
126                if incr != 1 {
127                    write!(f, "..{incr}")?;
128                }
129            }
130        }
131        f.write_str("}")?;
132        Ok(())
133    }
134}
135
136/// Bash-style brace expression. Can be created using TryFrom (like
137/// `"foo{bar,baz}biz".try_into()`) or via FromStr
138/// (`"foo{bar,baz}biz".parse()`). TryFrom is preferred, because it will avoid
139/// unnecessary allocations wherever possible, and tie to the lifetime of the
140/// incoming string. FromStr will make String clones in unnecessary places.
141#[derive(Clone, Debug)]
142pub struct Expression<'a>(Vec<Part<'a>>);
143
144impl<'a> Expression<'a> {
145    fn into_owned(self) -> Expression<'static> {
146        Expression(self.0.into_iter().map(Part::into_owned).collect())
147    }
148}
149
150impl FromStr for Expression<'static> {
151    type Err = String;
152
153    fn from_str(s: &str) -> Result<Self, Self::Err> {
154        let expression: Expression = s.try_into()?;
155        Ok(expression.into_owned())
156    }
157}
158
159impl<'a> TryFrom<&'a str> for Expression<'a> {
160    type Error = String;
161
162    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
163        let output = parser::expression::<VerboseError<&str>>(value);
164        match output {
165            Ok((_, expression)) => Ok(expression),
166            Err(nom::Err::Error(e) | nom::Err::Failure(e)) => return Err(convert_error(value, e)),
167            _ => panic!("Somehow got an incomplete"),
168        }
169    }
170}
171
172impl<'a> IntoIterator for Expression<'a> {
173    type Item = Result<Cow<'a, str>, CharTryFromError>;
174
175    type IntoIter = ExpressionIterator<'a>;
176
177    fn into_iter(self) -> Self::IntoIter {
178        ExpressionIterator(self.0.into_iter().multi_cartesian_product())
179    }
180}
181
182impl std::fmt::Display for Expression<'_> {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        for part in &self.0 {
185            write!(f, "{part}")?;
186        }
187        Ok(())
188    }
189}
190
191#[derive(Clone, Debug)]
192pub struct ExpressionIterator<'a>(MultiProduct<PartIterator<'a>>);
193
194impl<'a> Iterator for ExpressionIterator<'a> {
195    type Item = Result<Cow<'a, str>, CharTryFromError>;
196
197    fn next(&mut self) -> Option<Self::Item> {
198        self.0.next().map(|parts| match parts.len() {
199            0 => Ok(Cow::Borrowed("")),
200            1 => parts.into_iter().next().unwrap(),
201            _ => {
202                let parts: Result<Vec<_>, _> = parts.into_iter().collect();
203                let parts = parts?;
204                let mut string = String::with_capacity(parts.iter().map(|s| s.len()).sum());
205                for part in parts {
206                    string.push_str(&part);
207                }
208                Ok(Cow::Owned(string))
209            }
210        })
211    }
212}
213
214#[derive(Clone, Debug)]
215enum Part<'a> {
216    Plain(Cow<'a, str>),
217    List(List<'a>),
218    Sequence(Sequence),
219    Expression(Expression<'a>),
220}
221
222impl<'a> Part<'a> {
223    fn into_owned(self) -> Part<'static> {
224        match self {
225            Part::Plain(part) => Part::Plain(Cow::Owned(part.into_owned())),
226            Part::List(part) => Part::List(part.into_owned()),
227            Part::Sequence(part) => Part::Sequence(part),
228            Part::Expression(part) => Part::Expression(part.into_owned()),
229        }
230    }
231}
232
233#[derive(Clone, Debug)]
234enum PartIterator<'a> {
235    Plain(iter::Once<Cow<'a, str>>),
236    List(Box<<List<'a> as IntoIterator>::IntoIter>),
237    Sequence(<Sequence as IntoIterator>::IntoIter),
238    Expression(<Expression<'a> as IntoIterator>::IntoIter),
239}
240
241impl<'a> IntoIterator for Part<'a> {
242    type Item = Result<Cow<'a, str>, CharTryFromError>;
243
244    type IntoIter = PartIterator<'a>;
245
246    fn into_iter(self) -> Self::IntoIter {
247        match self {
248            Part::Plain(part) => PartIterator::Plain(iter::once(part.clone())),
249            Part::List(part) => PartIterator::List(Box::new(part.into_iter())),
250            Part::Sequence(part) => PartIterator::Sequence(part.into_iter()),
251            Part::Expression(part) => PartIterator::Expression(part.into_iter()),
252        }
253    }
254}
255
256impl<'a> Iterator for PartIterator<'a> {
257    type Item = Result<Cow<'a, str>, CharTryFromError>;
258
259    fn next(&mut self) -> Option<Self::Item> {
260        match self {
261            PartIterator::Plain(part) => part.next().map(|s| Ok(s)),
262            PartIterator::List(part) => part.next(),
263            PartIterator::Sequence(part) => part.next().map(|r| r.map(|s| Cow::Owned(s))),
264            PartIterator::Expression(part) => part.next(),
265        }
266    }
267}
268
269impl std::fmt::Display for Part<'_> {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271        match self {
272            Self::Plain(s) => {
273                for c in s.chars() {
274                    if ",{}\\".contains(c) {
275                        f.write_str("\\")?;
276                    }
277                    write!(f, "{c}")?;
278                }
279            }
280            Self::List(l) => write!(f, "{l}")?,
281            Self::Sequence(s) => write!(f, "{s}")?,
282            Self::Expression(e) => write!(f, "{e}")?,
283        }
284        Ok(())
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291
292    #[test]
293    fn test_simple_list() {
294        let expression: Expression = "{a,b,c}".try_into().unwrap();
295        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
296        let expected: Vec<_> = vec!["a", "b", "c"];
297        assert_eq!(generated.unwrap(), expected);
298    }
299    #[test]
300    fn test_simple_list_empties() {
301        let expression: Expression = "{a,,,b,c}".try_into().unwrap();
302        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
303        let expected: Vec<_> = vec!["a", "", "", "b", "c"];
304        assert_eq!(generated.unwrap(), expected);
305    }
306
307    #[test]
308    fn test_list_escapes() {
309        let expression: Expression = r"{a,b\,c,d\{e,f\}\\g}".try_into().unwrap();
310        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
311        let expected: Vec<_> = vec!["a", "b,c", "d{e", r"f}\g"];
312        assert_eq!(generated.unwrap(), expected);
313    }
314    #[test]
315    fn test_nested_list() {
316        let expression: Expression = r"s{a,b{,c,d{e,f}g,h{i,j{k}l,m{}n}o}p,q}r"
317            .try_into()
318            .unwrap();
319        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
320        let expected: Vec<_> = vec![
321            "sar",
322            "sbpr",
323            "sbcpr",
324            "sbdegpr",
325            "sbdfgpr",
326            "sbhiopr",
327            "sbhjklopr",
328            "sbhmnopr",
329            "sqr",
330        ];
331        assert_eq!(generated.unwrap(), expected);
332    }
333    #[test]
334    fn test_list_with_empty_part() {
335        let expression: Expression = "{a,,c}".try_into().unwrap();
336        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
337        let expected: Vec<_> = vec!["a", "", "c"];
338        assert_eq!(generated.unwrap(), expected);
339    }
340    #[test]
341    fn test_expression() {
342        let expression: Expression = "a{b,c,}d{1..2}e".try_into().unwrap();
343        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
344        let expected = vec!["abd1e", "abd2e", "acd1e", "acd2e", "ad1e", "ad2e"];
345        assert_eq!(generated.unwrap(), expected);
346    }
347    #[test]
348    fn test_char_sequence() {
349        let expression: Expression = "a{d..f}g".try_into().unwrap();
350        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
351        let expected = vec!["adg", "aeg", "afg"];
352        assert_eq!(generated.unwrap(), expected);
353    }
354    #[test]
355    fn test_negative_number_sequence() {
356        let expression: Expression = "a{-10..10..3}g".try_into().unwrap();
357        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
358        let expected = vec!["a-10g", "a-7g", "a-4g", "a-1g", "a2g", "a5g", "a8g"];
359        assert_eq!(generated.unwrap(), expected);
360    }
361    #[test]
362    fn test_decreasing_negative_number_sequence() {
363        let expression: Expression = "a{-10..10..3}g".try_into().unwrap();
364        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
365        let expected = vec!["a-10g", "a-7g", "a-4g", "a-1g", "a2g", "a5g", "a8g"];
366        assert_eq!(generated.unwrap(), expected);
367    }
368    #[test]
369    fn test_escaped_char_sequence() {
370        let expression: Expression = r"a{z..\}}b{\...\{..77}c".try_into().unwrap();
371        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
372        let expected = vec![
373            "azb.c", "azb{c", "a{b.c", "a{b{c", "a|b.c", "a|b{c", "a}b.c", "a}b{c",
374        ];
375        assert_eq!(generated.unwrap(), expected);
376    }
377
378    #[test]
379    fn test_equal_width_negative() {
380        let expression: Expression = r"{=-1..1000..300}".try_into().unwrap();
381        let generated: Result<Vec<_>, _> = expression.into_iter().collect();
382        let expected = vec!["-001", "0299", "0599", "0899"];
383        assert_eq!(generated.unwrap(), expected);
384    }
385
386    #[test]
387    fn test_display() {
388        let test_cases = [
389            "{a,b,c}",
390            "{a,,,b,c}",
391            r"{a,b\,c,d\{e,f\}\\g}",
392            r"s{a,b{,c,d{e,f}g,h{i,j{k}l,m{}n}o}p,q}r",
393            "{a,,c}",
394            "a{b,c,}d{1..2}e",
395            "a{d..f}g",
396            "a{-10..10..3}g",
397            "a{-10..10..3}g",
398            r"a{z..\}}b{\...\{..77}c",
399            r"{=-1..1000..300}",
400        ];
401        for test_case in test_cases {
402            assert_eq!(
403                Expression::try_from(test_case).unwrap().to_string(),
404                test_case,
405            );
406        }
407    }
408}