#![allow(clippy::type_complexity)]
#[macro_use]
pub(crate) mod macros;
pub(crate) mod array;
pub(crate) mod datetime;
pub(crate) mod document;
pub(crate) mod errors;
pub(crate) mod inline_table;
pub(crate) mod key;
pub(crate) mod numbers;
pub(crate) mod state;
pub(crate) mod strings;
pub(crate) mod table;
pub(crate) mod trivia;
pub(crate) mod value;
pub use errors::TomlError;
pub(crate) fn parse_document(raw: &str) -> Result<crate::Document, TomlError> {
use prelude::*;
let b = new_input(raw);
let mut doc = document::document
.parse(b)
.finish()
.map_err(|e| TomlError::new(e, b))?;
doc.span = Some(0..(raw.len()));
doc.original = Some(raw.to_owned());
Ok(doc)
}
pub(crate) fn parse_key(raw: &str) -> Result<crate::Key, TomlError> {
use prelude::*;
let b = new_input(raw);
let result = key::simple_key.parse(b).finish();
match result {
Ok((raw, key)) => {
Ok(crate::Key::new(key).with_repr_unchecked(crate::Repr::new_unchecked(raw)))
}
Err(e) => Err(TomlError::new(e, b)),
}
}
pub(crate) fn parse_key_path(raw: &str) -> Result<Vec<crate::Key>, TomlError> {
use prelude::*;
let b = new_input(raw);
let result = key::key.parse(b).finish();
match result {
Ok(mut keys) => {
for key in &mut keys {
key.despan(raw);
}
Ok(keys)
}
Err(e) => Err(TomlError::new(e, b)),
}
}
pub(crate) fn parse_value(raw: &str) -> Result<crate::Value, TomlError> {
use prelude::*;
let b = new_input(raw);
let parsed = value::value(RecursionCheck::default()).parse(b).finish();
match parsed {
Ok(mut value) => {
value.decor_mut().clear();
value.despan(raw);
Ok(value)
}
Err(e) => Err(TomlError::new(e, b)),
}
}
pub(crate) mod prelude {
pub(crate) use super::errors::Context;
pub(crate) use super::errors::ParserError;
pub(crate) use super::errors::ParserValue;
pub(crate) use nom8::IResult;
pub(crate) use nom8::Parser as _;
pub(crate) use nom8::FinishIResult as _;
pub(crate) type Input<'b> = nom8::input::Located<&'b [u8]>;
pub(crate) fn new_input(s: &str) -> Input<'_> {
nom8::input::Located::new(s.as_bytes())
}
pub(crate) fn ok_error<I, O, E>(res: IResult<I, O, E>) -> Result<Option<(I, O)>, nom8::Err<E>> {
match res {
Ok(ok) => Ok(Some(ok)),
Err(nom8::Err::Error(_)) => Ok(None),
Err(err) => Err(err),
}
}
#[allow(dead_code)]
pub(crate) fn trace<I: std::fmt::Debug, O: std::fmt::Debug, E: std::fmt::Debug>(
context: impl std::fmt::Display,
mut parser: impl nom8::Parser<I, O, E>,
) -> impl FnMut(I) -> IResult<I, O, E> {
static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
move |input: I| {
let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst) * 2;
eprintln!("{:depth$}--> {} {:?}", "", context, input);
match parser.parse(input) {
Ok((i, o)) => {
DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
eprintln!("{:depth$}<-- {} {:?}", "", context, i);
Ok((i, o))
}
Err(err) => {
DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
eprintln!("{:depth$}<-- {} {:?}", "", context, err);
Err(err)
}
}
}
}
#[cfg(not(feature = "unbounded"))]
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct RecursionCheck {
current: usize,
}
#[cfg(not(feature = "unbounded"))]
impl RecursionCheck {
pub(crate) fn check_depth(depth: usize) -> Result<(), super::errors::CustomError> {
if depth < 128 {
Ok(())
} else {
Err(super::errors::CustomError::RecursionLimitExceeded)
}
}
pub(crate) fn recursing(
mut self,
input: Input<'_>,
) -> Result<Self, nom8::Err<ParserError<'_>>> {
self.current += 1;
if self.current < 128 {
Ok(self)
} else {
Err(nom8::Err::Error(
nom8::error::FromExternalError::from_external_error(
input,
nom8::error::ErrorKind::Eof,
super::errors::CustomError::RecursionLimitExceeded,
),
))
}
}
}
#[cfg(feature = "unbounded")]
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct RecursionCheck {}
#[cfg(feature = "unbounded")]
impl RecursionCheck {
pub(crate) fn check_depth(_depth: usize) -> Result<(), super::errors::CustomError> {
Ok(())
}
pub(crate) fn recursing(
self,
_input: Input<'_>,
) -> Result<Self, nom8::Err<ParserError<'_>>> {
Ok(self)
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn documents() {
let documents = [
"",
r#"
# This is a TOML document.
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00 # First class dates
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
[servers]
# Indentation (tabs and/or spaces) is allowed but not required
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ]
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]
'some.wierd .stuff' = """
like
that
# """ # this broke my sintax highlighting
" also. like " = '''
that
'''
double = 2e39 # this number looks familiar
# trailing comment"#,
r#""#,
r#" "#,
r#" hello = 'darkness' # my old friend
"#,
r#"[parent . child]
key = "value"
"#,
r#"hello.world = "a"
"#,
r#"foo = 1979-05-27 # Comment
"#,
];
for input in documents {
dbg!(input);
let mut parsed = parse_document(input);
if let Ok(parsed) = &mut parsed {
parsed.despan();
}
let doc = match parsed {
Ok(doc) => doc,
Err(err) => {
panic!(
"Parse error: {:?}\nFailed to parse:\n```\n{}\n```",
err, input
)
}
};
snapbox::assert_eq(input, doc.to_string());
}
}
#[test]
fn documents_parse_only() {
let parse_only = ["\u{FEFF}
[package]
name = \"foo\"
version = \"0.0.1\"
authors = []
"];
for input in parse_only {
dbg!(input);
let mut parsed = parse_document(input);
if let Ok(parsed) = &mut parsed {
parsed.despan();
}
match parsed {
Ok(_) => (),
Err(err) => {
panic!(
"Parse error: {:?}\nFailed to parse:\n```\n{}\n```",
err, input
)
}
}
}
}
#[test]
fn invalid_documents() {
let invalid_inputs = [r#" hello = 'darkness' # my old friend
$"#];
for input in invalid_inputs {
dbg!(input);
let mut parsed = parse_document(input);
if let Ok(parsed) = &mut parsed {
parsed.despan();
}
assert!(parsed.is_err(), "Input: {:?}", input);
}
}
}