hocon-rs 0.2.0

Rust implementation of HOCON
Documentation
use crate::Result;
use crate::error::Error;
use crate::parser::HoconParser;
use crate::parser::read::Read;
use crate::raw::substitution::Substitution;

impl<'de, R: Read<'de>> HoconParser<R> {
    pub(crate) fn parse_substitution(&mut self) -> Result<Substitution> {
        let (ch1, ch2) = self.reader.peek2()?;
        if ch1 != b'$' {
            return Err(Error::UnexpectedToken {
                expected: "$",
                found_beginning: ch1,
            });
        }
        self.reader.discard(1)?;
        if ch2 != b'{' {
            return Err(Error::UnexpectedToken {
                expected: "{",
                found_beginning: ch2,
            });
        }
        self.reader.discard(1)?;
        let ch = self.reader.peek()?;
        let optional = if ch == b'?' {
            self.reader.discard(1)?;
            true
        } else {
            false
        };
        self.drop_horizontal_whitespace()?;
        let path_expression = self.parse_path_expression()?;
        let ch = self.reader.peek()?;
        if ch != b'}' {
            return Err(Error::UnexpectedToken {
                expected: "}",
                found_beginning: ch,
            });
        }
        self.reader.discard(1)?;
        let substitution = Substitution::new(path_expression, optional);
        Ok(substitution)
    }
}

#[cfg(test)]
mod tests {
    use crate::{Result, parser::read::StreamRead};
    use std::io::BufReader;

    use crate::parser::HoconParser;
    use rstest::rstest;

    #[rstest]
    #[case("${a}", "${a}")]
    #[case("${foo .bar }", "${foo .bar}")]
    #[case(r#"${a. b."c"}"#, "${a. b.c}")]
    #[case(r#"${? a. b."c"}"#, "${?a. b.c}")]
    #[case(r#"${? """a""". b."c"}"#, "${?a. b.c}")]
    fn test_valid_path_expression(#[case] input: &str, #[case] expected: &str) -> Result<()> {
        let read = StreamRead::new(BufReader::new(input.as_bytes()));
        let mut parser = HoconParser::new(read);
        let substitution = parser.parse_substitution()?;
        assert_eq!(substitution.to_string(), expected);
        Ok(())
    }

    #[rstest]
    #[case("${foo .bar")]
    #[case("${ ?foo.bar}")]
    #[case("${?foo.bar.}")]
    #[case("${?foo.bar")]
    fn test_invalid_path_expression(#[case] input: &str) {
        let read = StreamRead::new(BufReader::new(input.as_bytes()));
        let mut parser = HoconParser::new(read);
        let result = parser.parse_substitution();
        assert!(result.is_err());
    }
}