Note: This site is currently "Under construction". I'm migrating to a new version of my site building software. Lots of things are in a state of disrepair as a result (for example, footnote links aren't working). It's all part of the process of building in public. Most things should still be readable though.

Parsing And Converting Text With the nom Rust Crate

Code

#![allow(warnings)]
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_until;
use nom::combinator::eof;
use nom::combinator::rest;
use nom::multi::many_till;
use nom::sequence::tuple;
use nom::IResult;
use nom::Parser;

fn main() {
    let source = "alfa <b>bravo</b> - <b>charlie</b> delta";
    let expected = "alfa *bravo* - *charlie* delta";
    let (_, result) = convert(source).unwrap();
    assert_eq!(expected, result);

    let source = "echo <i>foxtrot</i> <b>golf</b> hotel <i>india</i>";
    let expected = "echo _foxtrot_ *golf* hotel _india_";
    let (_, result) = convert(source).unwrap();
    assert_eq!(expected, result);

    let source = "<i>juliett</i> <b>kilo</b> lima <i>mike</i>";
    let expected = "_juliett_ *kilo* lima _mike_";
    let (_, result) = convert(source).unwrap();
    assert_eq!(expected, result);
}

fn convert(source: &str) -> IResult<&str, String> {
    let (_, results) = many_till(grinder, eof)(source)?;
    Ok(("", results.0.join("")))
}

fn grinder(source: &str) -> IResult<&str, String> {
    let (active_string, preface) = alt((take_until("<"), rest))(source)?;
    let (next_up, completed) = alt((
        tuple((tag("<b>"), take_until("</b>"), tag("</b>"))).map(|x| format!("*{}*", &x.1)),
        tuple((tag("<i>"), take_until("</i>"), tag("</i>"))).map(|x| format!("_{}_", &x.1)),
        rest.map(|x: &str| x.to_string()),
    ))(active_string)?;
    Ok((next_up, format!("{}{}", preface, completed)))
}