mod array;
mod comment;
mod include;
mod object;
pub mod read;
mod string;
mod substitution;
use crate::Result;
use crate::config_options::ConfigOptions;
use crate::error::Error;
use crate::parser::read::Read;
use crate::raw::raw_object::RawObject;
#[derive(Debug)]
pub struct HoconParser<R> {
pub(crate) reader: R,
pub(crate) scratch: Vec<u8>,
pub(crate) options: ConfigOptions,
pub(crate) depth: usize,
}
impl<'de, R: Read<'de>> HoconParser<R> {
pub fn new(reader: R) -> Self {
HoconParser {
reader,
scratch: vec![],
options: Default::default(),
depth: 0,
}
}
pub fn with_options(reader: R, options: ConfigOptions) -> Self {
HoconParser {
reader,
scratch: vec![],
options,
depth: 0,
}
}
pub(crate) fn parse_horizontal_whitespace(&mut self, scratch: &mut Vec<u8>) -> Result<()> {
loop {
match self.reader.peek_horizontal_whitespace() {
Ok(Some(n)) => {
for _ in 0..n {
let byte = self.reader.next()?;
scratch.push(byte);
}
}
Ok(None) | Err(Error::Eof) => break,
Err(err) => return Err(err),
}
}
Ok(())
}
pub(crate) fn drop_horizontal_whitespace(&mut self) -> Result<()> {
loop {
match self.reader.peek_horizontal_whitespace() {
Ok(Some(n)) => {
self.reader.discard(n)?;
}
Ok(None) | Err(Error::Eof) => break,
Err(err) => return Err(err),
}
}
Ok(())
}
pub(crate) fn drop_whitespace(&mut self) -> Result<()> {
loop {
match self.reader.peek_whitespace() {
Ok(Some(n)) => {
self.reader.discard(n)?;
}
Ok(None) | Err(Error::Eof) => break,
Err(err) => return Err(err),
}
}
Ok(())
}
pub(crate) fn drop_comma_separator(&mut self) -> Result<bool> {
match self.reader.peek() {
Ok(ch) => {
if ch == b',' {
self.reader.discard(1)?;
}
}
Err(Error::Eof) => return Ok(true),
Err(err) => {
return Err(err);
}
}
Ok(false)
}
pub fn parse(&mut self) -> Result<RawObject> {
self.drop_whitespace_and_comments()?;
let raw_obj = match self.reader.peek() {
Ok(ch) => {
if ch == b'{' {
self.parse_object(false)?
} else {
self.parse_braces_omitted_object()?
}
}
Err(Error::Eof) => {
return Ok(RawObject::default());
}
Err(err) => {
return Err(err);
}
};
self.drop_whitespace_and_comments()?;
match self.reader.peek() {
Ok(ch) => {
return Err(Error::UnexpectedToken {
expected: "end of file",
found_beginning: ch,
});
}
Err(Error::Eof) => {}
Err(err) => {
return Err(err);
}
}
Ok(raw_obj)
}
}
#[cfg(test)]
mod tests {
use std::io::BufReader;
use crate::Result;
use crate::config_options::ConfigOptions;
use crate::parser::HoconParser;
use crate::parser::read::StreamRead;
use rstest::rstest;
#[rstest]
#[case("resources/base.conf")]
#[case("resources/concat.conf")]
#[case("resources/concat2.conf")]
#[case("resources/concat3.conf")]
#[case("resources/demo.conf")]
#[case("resources/deserialize.conf")]
#[case("resources/empty.conf")]
#[case("resources/included.conf")]
#[case("resources/main.conf")]
fn test_parse(#[case] path: impl AsRef<std::path::Path>) -> Result<()> {
let file = std::fs::File::open(&path)?;
let read = StreamRead::new(BufReader::new(file));
let options = ConfigOptions::new(false, vec!["resources".to_string()]);
let mut parser = HoconParser::with_options(read, options);
parser.parse()?;
Ok(())
}
}