miniconf-parser 0.1.3

Pest-powered parser and CLI for the MiniConf configuration format.
Documentation
use anyhow::{Result, anyhow};
use miniconf_parser::parser::{MiniConfParser, Rule};
use pest::Parser;
use pest::iterators::Pair;

fn parse_pair<'input>(rule: Rule, input: &'input str) -> Result<Pair<'input, Rule>> {
    let mut pairs = MiniConfParser::parse(rule, input)?;
    pairs
        .next()
        .ok_or_else(|| anyhow!("no pair matched for {rule:?}"))
}

#[test]
fn section_header_parses_hyphenated_names() -> Result<()> {
    let input = "[build-target]";
    let pair = parse_pair(Rule::section_header, input)?;
    assert_eq!(pair.as_str(), input);
    Ok(())
}

#[test]
fn key_value_allows_inline_comments() -> Result<()> {
    let input = r#"name = "alpha" # inline comment"#;
    let pair = parse_pair(Rule::key_value, input)?;
    assert_eq!(pair.as_str(), input);
    Ok(())
}

#[test]
fn value_rule_supports_all_atoms() -> Result<()> {
    let cases = ["\"quoted\"", "bare_word", "42", "-3.14", "true", "no"];

    for input in cases {
        let pair = parse_pair(Rule::value, input)?;
        assert_eq!(pair.as_str(), input);
    }
    Ok(())
}

#[test]
fn array_rule_preserves_nested_values() -> Result<()> {
    let pair = parse_pair(Rule::array, "[one, 2, \"three\"]")?;
    let values = pair
        .into_inner()
        .filter(|inner| inner.as_rule() == Rule::value)
        .count();
    assert_eq!(values, 3);
    Ok(())
}

#[test]
fn text_rule_handles_sections_and_root() -> Result<()> {
    let input = r#"
    # doc header
    title = Example
    enabled = true

    [database]
    host = localhost
    tags = [primary, read]
    "#;

    let pair = parse_pair(Rule::text, input)?;
    let text = pair.as_str();
    assert!(text.contains("title = Example"));
    assert!(text.contains("[database]"));
    Ok(())
}