| use crate::document::Document; |
| use crate::key::Key; |
| use crate::parser::errors::CustomError; |
| use crate::parser::inline_table::KEYVAL_SEP; |
| use crate::parser::key::key; |
| use crate::parser::table::table; |
| use crate::parser::trivia::{comment, line_ending, line_trailing, newline, ws}; |
| use crate::parser::value::value; |
| use crate::parser::{TomlError, TomlParser}; |
| use crate::table::TableKeyValue; |
| use crate::{InternalString, Item}; |
| use combine::parser::byte::byte; |
| use combine::stream::position::{IndexPositioner, Positioner, Stream}; |
| use combine::stream::RangeStream; |
| use combine::Parser; |
| use combine::*; |
| use indexmap::map::Entry; |
| use std::cell::RefCell; |
| use std::mem; |
| use std::ops::DerefMut; |
| |
| toml_parser!(parse_comment, parser, { |
| (comment(), line_ending()).and_then::<_, _, std::str::Utf8Error>(|(c, e)| { |
| let c = std::str::from_utf8(c)?; |
| parser.borrow_mut().deref_mut().on_comment(c, e); |
| Ok(()) |
| }) |
| }); |
| |
| toml_parser!( |
| parse_ws, |
| parser, |
| ws().map(|w| parser.borrow_mut().deref_mut().on_ws(w)) |
| ); |
| |
| toml_parser!(parse_newline, parser, { |
| newline().map(|_| parser.borrow_mut().deref_mut().on_ws("\n")) |
| }); |
| |
| toml_parser!(keyval, parser, { |
| parse_keyval().and_then(|(p, kv)| parser.borrow_mut().deref_mut().on_keyval(p, kv)) |
| }); |
| |
| // keyval = key keyval-sep val |
| parser! { |
| fn parse_keyval['a, I]()(I) -> (Vec<Key>, TableKeyValue) |
| where |
| [I: RangeStream< |
| Range = &'a [u8], |
| Token = u8>, |
| I::Error: ParseError<u8, &'a [u8], <I as StreamOnce>::Position>, |
| <I::Error as ParseError<u8, &'a [u8], <I as StreamOnce>::Position>>::StreamError: |
| From<std::num::ParseIntError> + |
| From<std::num::ParseFloatError> + |
| From<std::str::Utf8Error> + |
| From<crate::parser::errors::CustomError> |
| ] { |
| ( |
| key(), |
| byte(KEYVAL_SEP), |
| (ws(), value(), line_trailing()) |
| ).and_then::<_, _, std::str::Utf8Error>(|(key, _, v)| { |
| let mut path = key; |
| let key = path.pop().expect("grammar ensures at least 1"); |
| |
| let (pre, v, suf) = v; |
| let suf = std::str::from_utf8(suf)?; |
| let v = v.decorated(pre, suf); |
| Ok(( |
| path, |
| TableKeyValue { |
| key, |
| value: Item::Value(v), |
| } |
| )) |
| }) |
| } |
| } |
| |
| impl TomlParser { |
| // ;; TOML |
| |
| // toml = expression *( newline expression ) |
| |
| // expression = ( ( ws comment ) / |
| // ( ws keyval ws [ comment ] ) / |
| // ( ws table ws [ comment ] ) / |
| // ws ) |
| pub(crate) fn parse(s: &[u8]) -> Result<Document, TomlError> { |
| // Remove BOM if present |
| let s = s.strip_prefix(b"\xEF\xBB\xBF").unwrap_or(s); |
| |
| let mut parser = RefCell::new(Self::default()); |
| let input = Stream::new(s); |
| |
| let parsed = parse_ws(&parser) |
| .with(choice(( |
| eof(), |
| skip_many1( |
| look_ahead(any()).then(|e| { |
| dispatch!(e; |
| crate::parser::trivia::COMMENT_START_SYMBOL => parse_comment(&parser), |
| crate::parser::table::STD_TABLE_OPEN => table(&parser), |
| crate::parser::trivia::LF | |
| crate::parser::trivia::CR => parse_newline(&parser), |
| _ => keyval(&parser), |
| ) |
| }) |
| .skip(parse_ws(&parser)), |
| ), |
| ))) |
| .easy_parse(input); |
| match parsed { |
| Ok((_, ref rest)) if !rest.input.is_empty() => Err(TomlError::from_unparsed( |
| (&rest.positioner |
| as &dyn Positioner<usize, Position = usize, Checkpoint = IndexPositioner>) |
| .position(), |
| s, |
| )), |
| Ok(..) => { |
| parser |
| .get_mut() |
| .finalize_table() |
| .map_err(|e| TomlError::custom(e.to_string()))?; |
| let trailing = parser.borrow().trailing.as_str().into(); |
| parser.get_mut().document.trailing = trailing; |
| Ok(parser.into_inner().document) |
| } |
| Err(e) => Err(TomlError::new(e, s)), |
| } |
| } |
| |
| fn on_ws(&mut self, w: &str) { |
| self.trailing.push_str(w); |
| } |
| |
| fn on_comment(&mut self, c: &str, e: &str) { |
| self.trailing = [&self.trailing, c, e].concat(); |
| } |
| |
| fn on_keyval(&mut self, mut path: Vec<Key>, mut kv: TableKeyValue) -> Result<(), CustomError> { |
| { |
| let prefix = mem::take(&mut self.trailing); |
| let first_key = if path.is_empty() { |
| &mut kv.key |
| } else { |
| &mut path[0] |
| }; |
| first_key |
| .decor |
| .set_prefix(prefix + first_key.decor.prefix().unwrap_or_default()); |
| } |
| |
| let table = &mut self.current_table; |
| let table = Self::descend_path(table, &path, true)?; |
| |
| // "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed" |
| let mixed_table_types = table.is_dotted() == path.is_empty(); |
| if mixed_table_types { |
| return Err(CustomError::DuplicateKey { |
| key: kv.key.get().into(), |
| table: None, |
| }); |
| } |
| |
| let key: InternalString = kv.key.get_internal().into(); |
| match table.items.entry(key) { |
| Entry::Vacant(o) => { |
| o.insert(kv); |
| } |
| Entry::Occupied(o) => { |
| // "Since tables cannot be defined more than once, redefining such tables using a [table] header is not allowed" |
| return Err(CustomError::DuplicateKey { |
| key: o.key().as_str().into(), |
| table: Some(self.current_table_path.clone()), |
| }); |
| } |
| } |
| |
| Ok(()) |
| } |
| } |