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