1#![allow(clippy::type_complexity)]
2
3pub(crate) mod array;
4pub(crate) mod datetime;
5pub(crate) mod document;
6pub(crate) mod error;
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 crate::error::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::error::CustomError> {
99 if depth < 128 {
100 Ok(())
101 } else {
102 Err(super::error::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::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 {
129 pub(crate) fn check_depth(_depth: usize) -> Result<(), super::error::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)]
143#[cfg(feature = "parse")]
144#[cfg(feature = "display")]
145mod test {
146 use super::*;
147
148 #[test]
149 fn documents() {
150 let documents = [
151 "",
152 r#"
153# This is a TOML document.
154
155title = "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
182hosts = [
183 "alpha",
184 "omega"
185]
186
187 'some.weird .stuff' = """
188 like
189 that
190 # """ # this broke my syntax highlighting
191 " also. like " = '''
192that
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]
201key = "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]
229 fn documents_parse_only() {
230 let parse_only = ["\u{FEFF}
231[package]
232name = \"foo\"
233version = \"0.0.1\"
234authors = []
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]
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