blob: 2f4a442cdd5819908bedaa08d8c1302bfdc41801 [file] [log] [blame]
use crate::array_of_tables::ArrayOfTables;
use crate::decor::Decor;
use crate::key::Key;
use crate::parser::errors::CustomError;
use crate::parser::key::key;
use crate::parser::trivia::{line_trailing, ws};
use crate::parser::TomlParser;
use crate::table::{Item, Table};
use combine::parser::char::char;
use combine::parser::range::range;
use combine::stream::RangeStream;
use combine::*;
use std::cell::RefCell;
use std::mem;
// https://github.com/rust-lang/rust/issues/41358
#[allow(unused_imports)]
use std::ops::DerefMut;
// table-key-sep = ws %x2E ws ; . Period
const TABLE_KEY_SEP: char = '.';
// std-table-open = %x5B ws ; [ Left square bracket
const STD_TABLE_OPEN: char = '[';
// std-table-close = ws %x5D ; ] Right square bracket
const STD_TABLE_CLOSE: char = ']';
// array-table-open = %x5B.5B ws ; [[ Double left square bracket
const ARRAY_TABLE_OPEN: &str = "[[";
// array-table-close = ws %x5D.5D ; ]] Double right quare bracket
const ARRAY_TABLE_CLOSE: &str = "]]";
// note: this rule is not present in the original grammar
// key-path = key *( table-key-sep key)
parse!(key_path() -> Vec<Key>, {
sep_by1(between(ws(), ws(), key().map(|(raw, key)| Key::new(raw, key))),
char(TABLE_KEY_SEP))
});
// ;; Standard Table
// std-table = std-table-open key *( table-key-sep key) std-table-close
toml_parser!(std_table, parser, {
(
between(char(STD_TABLE_OPEN), char(STD_TABLE_CLOSE), key_path()),
line_trailing(),
)
.and_then(|(h, t)| parser.borrow_mut().deref_mut().on_std_header(&h, t))
});
// ;; Array Table
// array-table = array-table-open key *( table-key-sep key) array-table-close
toml_parser!(array_table, parser, {
(
between(
range(ARRAY_TABLE_OPEN),
range(ARRAY_TABLE_CLOSE),
key_path(),
),
line_trailing(),
)
.and_then(|(h, t)| parser.borrow_mut().deref_mut().on_array_header(&h, t))
});
// ;; Table
// table = std-table / array-table
parser! {
pub fn table['a, 'b, I](parser: &'b RefCell<TomlParser>)(I) -> ()
where
[I: RangeStream<
Range = &'a str,
Token = char>,
I::Error: ParseError<char, &'a str, <I as StreamOnce>::Position>,
<I::Error as ParseError<char, &'a str, <I as StreamOnce>::Position>>::StreamError:
From<std::num::ParseIntError> +
From<std::num::ParseFloatError> +
From<chrono::ParseError> +
From<crate::parser::errors::CustomError>
] {
array_table(parser)
.or(std_table(parser))
.message("While parsing a Table Header")
}
}
pub(crate) fn duplicate_key(path: &[Key], i: usize) -> CustomError {
assert!(i < path.len());
let header: Vec<&str> = path[..i].iter().map(Key::raw).collect();
CustomError::DuplicateKey {
key: path[i].raw().into(),
table: format!("[{}]", header.join(".")),
}
}
impl TomlParser {
pub(crate) fn descend_path<'a>(
table: &'a mut Table,
path: &[Key],
i: usize,
) -> Result<&'a mut Table, CustomError> {
if let Some(key) = path.get(i) {
let entry = table.entry(key.raw());
if entry.is_none() {
let mut new_table = Table::new();
new_table.set_implicit(true);
*entry = Item::Table(new_table);
}
match *entry {
Item::Value(..) => Err(duplicate_key(path, i)),
Item::ArrayOfTables(ref mut array) => {
debug_assert!(!array.is_empty());
let index = array.len() - 1;
let last_child = array.get_mut(index).unwrap();
Self::descend_path(last_child, path, i + 1)
}
Item::Table(ref mut sweet_child_of_mine) => {
TomlParser::descend_path(sweet_child_of_mine, path, i + 1)
}
_ => unreachable!(),
}
} else {
Ok(table)
}
}
fn on_std_header(&mut self, path: &[Key], trailing: &str) -> Result<(), CustomError> {
debug_assert!(!path.is_empty());
let leading = mem::take(&mut self.document.trailing);
let table = self.document.as_table_mut();
self.current_table_position += 1;
let table = Self::descend_path(table, &path[..path.len() - 1], 0);
let key = &path[path.len() - 1];
match table {
Ok(table) => {
let decor = Decor::new(leading, trailing.into());
let entry = table.entry(key.raw());
if entry.is_none() {
*entry = Item::Table(Table::with_decor_and_pos(
decor,
Some(self.current_table_position),
));
self.current_table_path = path.to_vec();
return Ok(());
}
match *entry {
// if [a.b.c] header preceded [a.b]
Item::Table(ref mut t) if t.implicit => {
debug_assert!(t.values_len() == 0);
t.decor = decor;
t.position = Some(self.current_table_position);
t.set_implicit(false);
self.current_table_path = path.to_vec();
return Ok(());
}
_ => {}
}
Err(duplicate_key(path, path.len() - 1))
}
Err(e) => Err(e),
}
}
fn on_array_header(&mut self, path: &[Key], trailing: &str) -> Result<(), CustomError> {
debug_assert!(!path.is_empty());
let leading = mem::take(&mut self.document.trailing);
let table = self.document.as_table_mut();
let key = &path[path.len() - 1];
let table = Self::descend_path(table, &path[..path.len() - 1], 0);
match table {
Ok(table) => {
if !table.contains_table(key.get()) && !table.contains_value(key.get()) {
let decor = Decor::new(leading, trailing.into());
let entry = table
.entry(key.raw())
.or_insert(Item::ArrayOfTables(ArrayOfTables::new()));
let array = entry.as_array_of_tables_mut().unwrap();
self.current_table_position += 1;
array.append(Table::with_decor_and_pos(
decor,
Some(self.current_table_position),
));
self.current_table_path = path.to_vec();
Ok(())
} else {
Err(duplicate_key(path, path.len() - 1))
}
}
Err(e) => Err(e),
}
}
}