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#[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}