toml_path/
toml_path.rs

1use eyre::Result;
2use std::str::FromStr;
3use thiserror::Error;
4use toml::{Table, Value};
5use winnow::ascii::alphanumeric1;
6use winnow::ascii::dec_int;
7use winnow::ascii::space0;
8use winnow::combinator::delimited;
9use winnow::combinator::repeat;
10use winnow::combinator::separated;
11use winnow::combinator::separated_pair;
12use winnow::combinator::seq;
13use winnow::prelude::*;
14use winnow::token::take_while;
15
16mod op;
17use op::op;
18pub use op::Index;
19pub use op::Op;
20pub use op::Range;
21
22/// TODO: doc comments
23/// Impls std::str::FromStr for convenience
24#[derive(Debug, Eq, PartialEq, Clone)]
25pub struct TomlPath {
26    parts: Vec<Op>,
27}
28
29impl TomlPath {
30    pub fn parts(&self) -> &[Op] {
31        &self.parts
32    }
33}
34
35fn path_parts(s: &mut &str) -> PResult<Vec<Op>> {
36    repeat(1.., op).parse_next(s)
37}
38
39fn toml_path(s: &mut &str) -> PResult<TomlPath> {
40    path_parts.map(|parts| TomlPath { parts }).parse_next(s)
41}
42
43#[derive(Error, Debug, Eq, PartialEq)]
44pub enum TomlPathError {
45    #[error("Unable to parse input str")]
46    UnableToParse,
47}
48
49impl FromStr for TomlPath {
50    type Err = TomlPathError;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        toml_path.parse(s).map_err(|_| TomlPathError::UnableToParse)
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use pretty_assertions::assert_eq;
61
62    #[test]
63    fn test_path_parts() {
64        let mut input = ".";
65        let expected = Ok(vec![Op::Dot]);
66        let result = path_parts.parse_next(&mut input);
67        assert_eq!(expected, result);
68        assert_eq!("", input);
69    }
70
71    #[test]
72    fn test_toml_path_dot() {
73        let input = ".";
74        let expected = Ok(TomlPath {
75            parts: vec![Op::Dot],
76        });
77        let result = TomlPath::from_str(input);
78        assert_eq!(expected, result);
79    }
80
81    #[test]
82    fn test_toml_path_dot_range() {
83        let input = ".[1:3]";
84        let expected = Ok(TomlPath {
85            parts: vec![
86                Op::Dot,
87                Op::BracketIndex(vec![Index::Range(Range::new(1, 3))]),
88            ],
89        });
90        let result = TomlPath::from_str(input);
91        assert_eq!(expected, result);
92    }
93
94    #[test]
95    fn test_toml_path_dot_list() {
96        let input = ".[1, 2, 3]";
97        let expected = Ok(TomlPath {
98            parts: vec![
99                Op::Dot,
100                Op::BracketIndex(vec![Index::Number(1), Index::Number(2), Index::Number(3)]),
101            ],
102        });
103        let result = TomlPath::from_str(input);
104        assert_eq!(expected, result);
105    }
106
107    #[test]
108    fn test_toml_path_dot_name() {
109        let input = ".foo";
110        let expected = Ok(TomlPath {
111            parts: vec![Op::Dot, Op::Name(String::from("foo"))],
112        });
113        let result = TomlPath::from_str(input);
114        assert_eq!(expected, result);
115    }
116
117    #[test]
118    fn test_toml_path_dot_names() {
119        let input = ".foo.bar.baz.qux";
120        let expected = Ok(TomlPath {
121            parts: vec![
122                Op::Dot,
123                Op::Name(String::from("foo")),
124                Op::Dot,
125                Op::Name(String::from("bar")),
126                Op::Dot,
127                Op::Name(String::from("baz")),
128                Op::Dot,
129                Op::Name(String::from("qux")),
130            ],
131        });
132        let result = TomlPath::from_str(input);
133        assert_eq!(expected, result);
134    }
135
136    #[test]
137    fn test_toml_path_dot_bracket_name() {
138        let input = ".[\"foo\"]";
139        let expected = Ok(TomlPath {
140            parts: vec![Op::Dot, Op::BracketName(vec![String::from("foo")])],
141        });
142        let result = TomlPath::from_str(input);
143        assert_eq!(expected, result);
144    }
145
146    #[test]
147    fn test_toml_path_dot_bracket_range() {
148        let input = ".[1:3]";
149        let expected = Ok(TomlPath {
150            parts: vec![
151                Op::Dot,
152                Op::BracketIndex(vec![Index::Range(Range::new(1, 3))]),
153            ],
154        });
155        let result = TomlPath::from_str(input);
156        assert_eq!(expected, result);
157    }
158
159    #[test]
160    fn test_toml_path_dot_bracket_number() {
161        let input = ".[1]";
162        let expected = Ok(TomlPath {
163            parts: vec![Op::Dot, Op::BracketIndex(vec![Index::Number(1)])],
164        });
165        let result = TomlPath::from_str(input);
166        assert_eq!(expected, result);
167    }
168}