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