toml_path/toml_path/
op.rs

1use eyre::bail;
2use eyre::Result;
3use std::fmt;
4use toml::{Table, Value};
5use winnow::ascii::alphanumeric1;
6use winnow::ascii::dec_int;
7use winnow::ascii::space0;
8use winnow::combinator::alt;
9use winnow::combinator::delimited;
10use winnow::combinator::opt;
11use winnow::combinator::separated;
12use winnow::combinator::separated_pair;
13use winnow::combinator::seq;
14use winnow::error::StrContext;
15use winnow::error::StrContextValue;
16use winnow::prelude::*;
17use winnow::token::take_while;
18
19// TODO: add winnnow contexts for better error messages to all parsers
20
21/// Range between two numbers.
22/// Negative numbers index from the end, where '-1' is the final item.
23#[derive(Debug, Eq, PartialEq, Clone)]
24pub struct Range {
25    start: isize,
26    end: isize,
27}
28
29impl Range {
30    /// Create a new Range from start and end indexes.
31    pub fn new(start: isize, end: isize) -> Self {
32        Self { start, end }
33    }
34
35    /// Generate all values in the range. Length is needed to compute the positive equivalent of negative indexes.
36    /// TODO: this needs some tests!
37    pub fn gen_range_indexes(&self, length: usize) -> Result<Vec<usize>> {
38        let length = length as isize;
39        let mut indexes: Vec<usize> = Vec::new();
40        // TODO: we could consider rewriting this match block where each branch returns the range and that range gets plugged into a single for loop at the end
41        match (self.start, self.end) {
42            (start, end) if start >= 0 && end < 0 => {
43                // Start positive/zero, end negative
44                let end_fixed = (length + end) as usize;
45                let start = start as usize;
46                if end_fixed < start {
47                    bail!("end < start ({:?})", self);
48                }
49
50                for n in start..=end_fixed {
51                    indexes.push(n);
52                }
53            }
54            (start, end) if start < 0 && end >= 0 => {
55                // Start negative and end positive/zero
56                let start_fixed = (length + start) as usize;
57                let end = end as usize;
58                if end < start_fixed {
59                    bail!("end < start ({:?})", self);
60                }
61
62                for n in start_fixed..end {
63                    indexes.push(n);
64                }
65            }
66            (start, end) if start >= 0 && end >= 0 => {
67                // Start and end both positive/zero
68                let start = start as usize;
69                let end = end as usize;
70                if end < start {
71                    bail!("end < start ({:?})", self);
72                }
73
74                for n in start..end {
75                    indexes.push(n);
76                }
77            }
78            (start, end) if start < 0 && end < 0 => {
79                // Start and end both negative
80                let start_fixed = (length + start) as usize;
81                let end_fixed = (length + end) as usize;
82                if end_fixed < start_fixed {
83                    bail!("end < start ({:?})", self);
84                }
85
86                for n in start_fixed..end_fixed {
87                    indexes.push(n);
88                }
89            }
90            _ => {
91                unreachable!("If this happens I have an error in my range logic");
92            }
93        }
94        Ok(indexes)
95    }
96}
97
98fn number(s: &mut &str) -> PResult<isize> {
99    dec_int.parse_next(s)
100}
101
102#[derive(Debug, Eq, PartialEq, Clone)]
103pub enum Index {
104    Number(isize),
105    Range(Range),
106}
107
108fn range(s: &mut &str) -> PResult<Range> {
109    separated_pair(number, space_colon_space, number)
110        .map(|(start, end)| Range { start, end })
111        .parse_next(s)
112}
113
114fn index_range(s: &mut &str) -> PResult<Index> {
115    range.map(|r| Index::Range(r)).parse_next(s)
116}
117
118fn index_number(s: &mut &str) -> PResult<Index> {
119    number.map(|x| Index::Number(x)).parse_next(s)
120}
121
122fn index(s: &mut &str) -> PResult<Index> {
123    // I think that range needs to be checked before number
124    alt((index_range, index_number)).parse_next(s)
125}
126
127#[derive(Debug, Eq, PartialEq, Clone)]
128pub enum Op {
129    /// "."
130    Dot,
131
132    /// Alphanumeric identifier
133    Name(String),
134
135    /// List of indicies and/or index ranges
136    /// [1, 2:4, 5]
137    /// TODO: rename this to 'Index' or something
138    BracketIndex(Vec<Index>),
139
140    /// List of alphanumeric identifiers
141    /// ["foo", "bar"]
142    /// TODO: rename this to 'NameIndex' or something
143    BracketName(Vec<String>),
144}
145
146impl fmt::Display for Op {
147    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148        let repr = match self {
149            Op::Dot => String::from("."),
150            Op::Name(string) => {
151                format!("Op::Name({:?})", string)
152            }
153            Op::BracketIndex(indexes) => {
154                format!("Op::BracketIndex({:?})", indexes)
155            }
156            Op::BracketName(names) => {
157                format!("Op::BracketName({:?})", names)
158            }
159        };
160
161        write!(f, "{}", repr)
162    }
163}
164
165fn dot(s: &mut &str) -> PResult<Op> {
166    ".".map(|_| Op::Dot).parse_next(s)
167}
168
169fn name_str(s: &mut &str) -> PResult<String> {
170    alphanumeric1.map(|s: &str| s.to_string()).parse_next(s)
171}
172
173fn name(s: &mut &str) -> PResult<Op> {
174    name_str.map(|name| Op::Name(name)).parse_next(s)
175}
176
177fn quoted_name(s: &mut &str) -> PResult<String> {
178    delimited("\"", name_str, "\"").parse_next(s)
179}
180
181fn comma_space(s: &mut &str) -> PResult<()> {
182    let _ = ",".parse_next(s)?;
183    let _ = space0.parse_next(s)?;
184    Ok(())
185}
186
187fn space_colon_space(s: &mut &str) -> PResult<()> {
188    let _ = space0.parse_next(s)?;
189    let _ = ":".parse_next(s)?;
190    let _ = space0.parse_next(s)?;
191    Ok(())
192}
193
194fn bracket_name_list(s: &mut &str) -> PResult<Op> {
195    let list = separated(1.., quoted_name, comma_space)
196        .map(|list| Op::BracketName(list))
197        .parse_next(s)?;
198    // Ignore possible trailing comma and space
199    let _ = opt(comma_space).parse_next(s)?;
200    Ok(list)
201}
202
203fn bracket_index_list(s: &mut &str) -> PResult<(Op)> {
204    let list = separated(1.., index, comma_space)
205        .map(|list| Op::BracketIndex(list))
206        .parse_next(s)?;
207    // Ignore possible trailing comma and space
208    let _ = opt(comma_space).parse_next(s)?;
209    Ok(list)
210}
211
212fn open_bracket_space(s: &mut &str) -> PResult<()> {
213    let _ = "[".parse_next(s)?;
214    let _ = space0.parse_next(s)?;
215    Ok(())
216}
217
218fn close_bracket_space(s: &mut &str) -> PResult<()> {
219    let _ = "]".parse_next(s)?;
220    let _ = space0.parse_next(s)?;
221    Ok(())
222}
223
224fn bracket(s: &mut &str) -> PResult<Op> {
225    delimited(
226        open_bracket_space,
227        alt((bracket_index_list, bracket_name_list)),
228        close_bracket_space,
229    )
230    .parse_next(s)
231}
232
233pub fn op(s: &mut &str) -> PResult<Op> {
234    alt((dot, name, bracket)).parse_next(s)
235}
236
237#[cfg(test)]
238mod tests {
239    use super::*;
240    use pretty_assertions::assert_eq;
241
242    macro_rules! s {
243        ($literal:expr) => {
244            String::from($literal)
245        };
246    }
247
248    // TODO: add tests for non-happy paths
249
250    #[test]
251    fn test_positive_number() {
252        let mut input = "1";
253    }
254
255    #[test]
256    fn test_zero() {
257        let mut input = "0";
258        let expected = Ok(0);
259        let result = number(&mut input);
260        assert_eq!(expected, result);
261        assert_eq!("", input);
262    }
263
264    #[test]
265    fn test_negative_number() {
266        let mut input = "-1";
267        let expected = Ok(-1);
268        let result = number(&mut input);
269        assert_eq!(expected, result);
270        assert_eq!("", input);
271    }
272
273    #[test]
274    fn test_space_colon_space_1() {
275        let mut input = ":";
276        let result = space_colon_space(&mut input);
277        assert_eq!(Ok(()), result);
278    }
279
280    #[test]
281    fn test_space_colon_space_2() {
282        let mut input = " :";
283        let result = space_colon_space(&mut input);
284        assert_eq!(Ok(()), result);
285    }
286
287    #[test]
288    fn test_space_colon_space_3() {
289        let mut input = ": ";
290        let result = space_colon_space(&mut input);
291        assert_eq!(Ok(()), result);
292    }
293
294    #[test]
295    fn test_space_colon_space_4() {
296        let mut input = " : ";
297        let result = space_colon_space(&mut input);
298        assert_eq!(Ok(()), result);
299    }
300
301    #[test]
302    fn test_range() {
303        let mut input = "1:3";
304        let expected = Ok(Range { start: 1, end: 3 });
305        let result = range(&mut input);
306        assert_eq!(expected, result);
307        assert_eq!("", input);
308    }
309
310    #[test]
311    fn test_bracket_range_list_one_item() {
312        let mut input = "1:4";
313        let expected = Ok(Op::BracketIndex(vec![Index::Range(Range::new(1, 4))]));
314        let result = bracket_index_list(&mut input);
315        assert_eq!(expected, result);
316        assert_eq!("", input);
317    }
318
319    #[test]
320    fn test_bracket_range_list_one_item_trailing_comma() {
321        let mut input = "1:4,";
322        let expected = Ok(Op::BracketIndex(vec![Index::Range(Range::new(1, 4))]));
323        let result = bracket_index_list(&mut input);
324        assert_eq!(expected, result);
325        assert_eq!("", input);
326    }
327
328    #[test]
329    fn test_bracket_range_list_multiple_items() {
330        let mut input = "1:2, 2:3, 3, 4";
331        let expected = Ok(Op::BracketIndex(vec![
332            Index::Range(Range::new(1, 2)),
333            Index::Range(Range::new(2, 3)),
334            Index::Number(3),
335            Index::Number(4),
336        ]));
337        let result = bracket_index_list(&mut input);
338        assert_eq!(expected, result);
339        assert_eq!("", input);
340    }
341
342    #[test]
343    fn test_bracket_range_list_no_space() {
344        let mut input = "1:1,2:2,3:3";
345        let expected = Ok(Op::BracketIndex(vec![
346            Index::Range(Range::new(1, 1)),
347            Index::Range(Range::new(2, 2)),
348            Index::Range(Range::new(3, 3)),
349        ]));
350        let result = bracket_index_list(&mut input);
351        assert_eq!(expected, result);
352        assert_eq!("", input);
353    }
354
355    #[test]
356    fn test_bracket_range_list_trailing_comma() {
357        let mut input = "0:1,1:2,2:3,";
358        let expected = Ok(Op::BracketIndex(vec![
359            Index::Range(Range::new(0, 1)),
360            Index::Range(Range::new(1, 2)),
361            Index::Range(Range::new(2, 3)),
362        ]));
363        let result = bracket_index_list(&mut input);
364        assert_eq!(expected, result);
365        assert_eq!("", input);
366    }
367
368    #[test]
369    fn test_bracket_range_list_negatives() {
370        let mut input = "0:1, 0:-2,-2, -3";
371        let expected = Ok(Op::BracketIndex(vec![
372            Index::Range(Range::new(0, 1)),
373            Index::Range(Range::new(0, -2)),
374            Index::Number(-2),
375            Index::Number(-3),
376        ]));
377        let result = bracket_index_list(&mut input);
378        assert_eq!(expected, result);
379        assert_eq!("", input);
380    }
381
382    #[test]
383    fn test_bracket_num_list_one_item() {
384        let mut input = "1";
385        let expected = Ok(Op::BracketIndex(vec![Index::Number(1)]));
386        let result = bracket_index_list(&mut input);
387        assert_eq!(expected, result);
388        assert_eq!("", input);
389    }
390
391    #[test]
392    fn test_bracket_num_list_one_item_trailing_comma() {
393        let mut input = "1,";
394        let expected = Ok(Op::BracketIndex(vec![Index::Number(1)]));
395        let result = bracket_index_list(&mut input);
396        assert_eq!(expected, result);
397        assert_eq!("", input);
398    }
399
400    #[test]
401    fn test_bracket_num_list_multiple_items() {
402        let mut input = "1, 2, 3";
403        let expected = Ok(Op::BracketIndex(vec![
404            Index::Number(1),
405            Index::Number(2),
406            Index::Number(3),
407        ]));
408        let result = bracket_index_list(&mut input);
409        assert_eq!(expected, result);
410        assert_eq!("", input);
411    }
412
413    #[test]
414    fn test_bracket_num_list_no_space() {
415        let mut input = "1,2,3";
416        let expected = Ok(Op::BracketIndex(vec![
417            Index::Number(1),
418            Index::Number(2),
419            Index::Number(3),
420        ]));
421        let result = bracket_index_list(&mut input);
422        assert_eq!(expected, result);
423        assert_eq!("", input);
424    }
425
426    #[test]
427    fn test_bracket_num_list_trailing_comma() {
428        let mut input = "1,2,3,";
429        let expected = Ok(Op::BracketIndex(vec![
430            Index::Number(1),
431            Index::Number(2),
432            Index::Number(3),
433        ]));
434        let result = bracket_index_list(&mut input);
435        assert_eq!(expected, result);
436        assert_eq!("", input);
437    }
438
439    #[test]
440    fn test_bracket_num_list_negatives() {
441        let mut input = "1, -2,-3";
442        let expected = Ok(Op::BracketIndex(vec![
443            Index::Number(1),
444            Index::Number(-2),
445            Index::Number(-3),
446        ]));
447        let result = bracket_index_list(&mut input);
448        assert_eq!(expected, result);
449        assert_eq!("", input);
450    }
451
452    #[test]
453    fn test_bracket_name_list_one_item() {
454        let mut input = "\"1\"";
455        let expected = Ok(Op::BracketName(vec![s!("1")]));
456        let result = bracket_name_list(&mut input);
457        assert_eq!(expected, result);
458        assert_eq!("", input);
459    }
460
461    #[test]
462    fn test_bracket_name_list_one_item_trailing_comma() {
463        let mut input = "\"1\",";
464        let expected = Ok(Op::BracketName(vec![s!("1")]));
465        let result = bracket_name_list(&mut input);
466        assert_eq!(expected, result);
467        assert_eq!("", input);
468    }
469
470    #[test]
471    fn test_bracket_name_list_multiple_items() {
472        let mut input = "\"1\", \"2\", \"3\"";
473        let expected = Ok(Op::BracketName(vec![s!("1"), s!("2"), s!("3")]));
474        let result = bracket_name_list(&mut input);
475        assert_eq!(expected, result);
476        assert_eq!("", input);
477    }
478
479    #[test]
480    fn test_bracket_name_list_no_space() {
481        let mut input = "\"1\",\"2\",\"3\"";
482        let expected = Ok(Op::BracketName(vec![s!("1"), s!("2"), s!("3")]));
483        let result = bracket_name_list(&mut input);
484        assert_eq!(expected, result);
485        assert_eq!("", input);
486    }
487
488    #[test]
489    fn test_bracket_name_list_trailing_comma() {
490        let mut input = "\"1\",\"2\",\"3\",";
491        let expected = Ok(Op::BracketName(vec![s!("1"), s!("2"), s!("3")]));
492        let result = bracket_name_list(&mut input);
493        assert_eq!(expected, result);
494        assert_eq!("", input);
495    }
496
497    #[test]
498    fn test_op_dot() {
499        let mut input = ".";
500        let expected = Ok(Op::Dot);
501        let result = op(&mut input);
502        assert_eq!(expected, result);
503        assert_eq!("", input);
504    }
505
506    #[test]
507    fn test_op_name() {
508        let mut input = "name";
509        let expected = Ok(Op::Name(String::from("name")));
510        let result = op(&mut input);
511        assert_eq!(expected, result);
512        assert_eq!("", input);
513    }
514
515    #[test]
516    fn test_op_bracket_name_list() {
517        let mut input = "[\"name\"]";
518        let expected = Ok(Op::BracketName(vec![String::from("name")]));
519        let result = op(&mut input);
520        assert_eq!(expected, result);
521        assert_eq!("", input);
522    }
523    #[test]
524    fn test_op_bracket_num_list() {
525        let mut input = "[1, 2, 3]";
526        let expected = Ok(Op::BracketIndex(vec![
527            Index::Number(1),
528            Index::Number(2),
529            Index::Number(3),
530        ]));
531        let result = op(&mut input);
532        assert_eq!(expected, result);
533        assert_eq!("", input);
534    }
535
536    #[test]
537    fn test_op_bracket_range() {
538        let mut input = "[1:4]";
539        let expected = Ok(Op::BracketIndex(vec![Index::Range(Range {
540            start: 1,
541            end: 4,
542        })]));
543        let result = op(&mut input);
544        assert_eq!(expected, result);
545        assert_eq!("", input);
546    }
547}