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