1 #![allow(clippy::type_complexity)]
2
3 pub(crate) mod array;
4 pub(crate) mod datetime;
5 pub(crate) mod document;
6 pub(crate) mod error;
7 pub(crate) mod inline_table;
8 pub(crate) mod key;
9 pub(crate) mod numbers;
10 pub(crate) mod state;
11 pub(crate) mod strings;
12 pub(crate) mod table;
13 pub(crate) mod trivia;
14 pub(crate) mod value;
15
16 pub use crate::error::TomlError;
17
parse_document(raw: &str) -> Result<crate::Document, TomlError>18 pub(crate) fn parse_document(raw: &str) -> Result<crate::Document, TomlError> {
19 use prelude::*;
20
21 let b = new_input(raw);
22 let mut doc = document::document
23 .parse(b)
24 .map_err(|e| TomlError::new(e, b))?;
25 doc.span = Some(0..(raw.len()));
26 doc.original = Some(raw.to_owned());
27 Ok(doc)
28 }
29
parse_key(raw: &str) -> Result<crate::Key, TomlError>30 pub(crate) fn parse_key(raw: &str) -> Result<crate::Key, TomlError> {
31 use prelude::*;
32
33 let b = new_input(raw);
34 let result = key::simple_key.parse(b);
35 match result {
36 Ok((raw, key)) => {
37 Ok(crate::Key::new(key).with_repr_unchecked(crate::Repr::new_unchecked(raw)))
38 }
39 Err(e) => Err(TomlError::new(e, b)),
40 }
41 }
42
parse_key_path(raw: &str) -> Result<Vec<crate::Key>, TomlError>43 pub(crate) fn parse_key_path(raw: &str) -> Result<Vec<crate::Key>, TomlError> {
44 use prelude::*;
45
46 let b = new_input(raw);
47 let result = key::key.parse(b);
48 match result {
49 Ok(mut keys) => {
50 for key in &mut keys {
51 key.despan(raw);
52 }
53 Ok(keys)
54 }
55 Err(e) => Err(TomlError::new(e, b)),
56 }
57 }
58
parse_value(raw: &str) -> Result<crate::Value, TomlError>59 pub(crate) fn parse_value(raw: &str) -> Result<crate::Value, TomlError> {
60 use prelude::*;
61
62 let b = new_input(raw);
63 let parsed = value::value(RecursionCheck::default()).parse(b);
64 match parsed {
65 Ok(mut value) => {
66 // Only take the repr and not decor, as its probably not intended
67 value.decor_mut().clear();
68 value.despan(raw);
69 Ok(value)
70 }
71 Err(e) => Err(TomlError::new(e, b)),
72 }
73 }
74
75 pub(crate) mod prelude {
76 pub(crate) use winnow::combinator::dispatch;
77 pub(crate) use winnow::error::ContextError;
78 pub(crate) use winnow::error::FromExternalError;
79 pub(crate) use winnow::error::StrContext;
80 pub(crate) use winnow::error::StrContextValue;
81 pub(crate) use winnow::PResult;
82 pub(crate) use winnow::Parser;
83
84 pub(crate) type Input<'b> = winnow::Located<&'b winnow::BStr>;
85
new_input(s: &str) -> Input<'_>86 pub(crate) fn new_input(s: &str) -> Input<'_> {
87 winnow::Located::new(winnow::BStr::new(s))
88 }
89
90 #[cfg(not(feature = "unbounded"))]
91 #[derive(Copy, Clone, Debug, Default)]
92 pub(crate) struct RecursionCheck {
93 current: usize,
94 }
95
96 #[cfg(not(feature = "unbounded"))]
97 impl RecursionCheck {
check_depth(depth: usize) -> Result<(), super::error::CustomError>98 pub(crate) fn check_depth(depth: usize) -> Result<(), super::error::CustomError> {
99 if depth < 128 {
100 Ok(())
101 } else {
102 Err(super::error::CustomError::RecursionLimitExceeded)
103 }
104 }
105
recursing( mut self, input: &mut Input<'_>, ) -> Result<Self, winnow::error::ErrMode<ContextError>>106 pub(crate) fn recursing(
107 mut self,
108 input: &mut Input<'_>,
109 ) -> Result<Self, winnow::error::ErrMode<ContextError>> {
110 self.current += 1;
111 if self.current < 128 {
112 Ok(self)
113 } else {
114 Err(winnow::error::ErrMode::from_external_error(
115 input,
116 winnow::error::ErrorKind::Eof,
117 super::error::CustomError::RecursionLimitExceeded,
118 ))
119 }
120 }
121 }
122
123 #[cfg(feature = "unbounded")]
124 #[derive(Copy, Clone, Debug, Default)]
125 pub(crate) struct RecursionCheck {}
126
127 #[cfg(feature = "unbounded")]
128 impl RecursionCheck {
check_depth(_depth: usize) -> Result<(), super::error::CustomError>129 pub(crate) fn check_depth(_depth: usize) -> Result<(), super::error::CustomError> {
130 Ok(())
131 }
132
recursing( self, _input: &mut Input<'_>, ) -> Result<Self, winnow::error::ErrMode<ContextError>>133 pub(crate) fn recursing(
134 self,
135 _input: &mut Input<'_>,
136 ) -> Result<Self, winnow::error::ErrMode<ContextError>> {
137 Ok(self)
138 }
139 }
140 }
141
142 #[cfg(test)]
143 #[cfg(feature = "parse")]
144 #[cfg(feature = "display")]
145 mod test {
146 use super::*;
147
148 #[test]
documents()149 fn documents() {
150 let documents = [
151 "",
152 r#"
153 # This is a TOML document.
154
155 title = "TOML Example"
156
157 [owner]
158 name = "Tom Preston-Werner"
159 dob = 1979-05-27T07:32:00-08:00 # First class dates
160
161 [database]
162 server = "192.168.1.1"
163 ports = [ 8001, 8001, 8002 ]
164 connection_max = 5000
165 enabled = true
166
167 [servers]
168
169 # Indentation (tabs and/or spaces) is allowed but not required
170 [servers.alpha]
171 ip = "10.0.0.1"
172 dc = "eqdc10"
173
174 [servers.beta]
175 ip = "10.0.0.2"
176 dc = "eqdc10"
177
178 [clients]
179 data = [ ["gamma", "delta"], [1, 2] ]
180
181 # Line breaks are OK when inside arrays
182 hosts = [
183 "alpha",
184 "omega"
185 ]
186
187 'some.weird .stuff' = """
188 like
189 that
190 # """ # this broke my syntax highlighting
191 " also. like " = '''
192 that
193 '''
194 double = 2e39 # this number looks familiar
195 # trailing comment"#,
196 r#""#,
197 r#" "#,
198 r#" hello = 'darkness' # my old friend
199 "#,
200 r#"[parent . child]
201 key = "value"
202 "#,
203 r#"hello.world = "a"
204 "#,
205 r#"foo = 1979-05-27 # Comment
206 "#,
207 ];
208 for input in documents {
209 dbg!(input);
210 let mut parsed = parse_document(input);
211 if let Ok(parsed) = &mut parsed {
212 parsed.despan();
213 }
214 let doc = match parsed {
215 Ok(doc) => doc,
216 Err(err) => {
217 panic!(
218 "Parse error: {:?}\nFailed to parse:\n```\n{}\n```",
219 err, input
220 )
221 }
222 };
223
224 snapbox::assert_eq(input, doc.to_string());
225 }
226 }
227
228 #[test]
documents_parse_only()229 fn documents_parse_only() {
230 let parse_only = ["\u{FEFF}
231 [package]
232 name = \"foo\"
233 version = \"0.0.1\"
234 authors = []
235 "];
236 for input in parse_only {
237 dbg!(input);
238 let mut parsed = parse_document(input);
239 if let Ok(parsed) = &mut parsed {
240 parsed.despan();
241 }
242 match parsed {
243 Ok(_) => (),
244 Err(err) => {
245 panic!(
246 "Parse error: {:?}\nFailed to parse:\n```\n{}\n```",
247 err, input
248 )
249 }
250 }
251 }
252 }
253
254 #[test]
invalid_documents()255 fn invalid_documents() {
256 let invalid_inputs = [r#" hello = 'darkness' # my old friend
257 $"#];
258 for input in invalid_inputs {
259 dbg!(input);
260 let mut parsed = parse_document(input);
261 if let Ok(parsed) = &mut parsed {
262 parsed.despan();
263 }
264 assert!(parsed.is_err(), "Input: {:?}", input);
265 }
266 }
267 }
268