Ramblings of an aging IT geek
← Ramblings of an aging IT geek
rust

parsing a config format with nom

A short note on using nom to parse a small custom config format in Rust, and why parser combinators clicked for me where hand-rolled string splitting hadn't.

Code on a screen, syntax highlighted

I needed to parse a small custom config format this week, a few key/value lines with nested blocks, and rather than reach for split and regret it, I used nom. I'd avoided parser combinators for years because the type signatures look like someone fell on the keyboard. Spending an evening with them, I get it now.

The mental model that unlocked it: a parser is just a function that takes a slice of input and returns the remaining input plus whatever it parsed. nom gives you tiny parsers, tag, take_while, digit1, and combinators to glue them together, tuple, alt, delimited, many0. You build a parser for a key, a parser for a value, then combine them into a parser for a line, then a parser for a block.

fn key_value(input: &str) -> IResult<&str, (&str, &str)> {
    separated_pair(
        alphanumeric1,
        delimited(space0, char('='), space0),
        is_not("\n"),
    )(input)
}

The payoff is that errors compose too. When the input doesn't match, nom tells you where, and you get that without writing a single index-juggling loop. The cost is the learning curve and the type ergonomics, which are genuinely awkward until they suddenly aren't. For anything beyond splitting on a delimiter, though, I'll reach for it again. It is far less error-prone than the hand-rolled string surgery it replaced.