Search code examples
parsingrustslugnom

Match a slug with Nom


I've been trying for some time to find a decent solution for Nom to recognize the slug as an alpha1. So I could parse something like this

fn parse<'a>(text: &'a str) -> IResult<&'a str, &'a str> {
  delimited(char(':'), slug, char(':'))(text)
}

assert!(
  parse(":hello-world-i-only-accept-alpha-numeric-char-and-dashes:"),
  "hello-world-i-only-accept-alpha-numeric-char-and-dashes"
);

I tried with something like this but it seems to doesn't work.

fn slug<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
where
    T: InputTakeAtPosition,
    <T as InputTakeAtPosition>::Item: AsChar + Clone,
{
    input.split_at_position1(
        |item| {
            let c = item.clone().as_char();

            !(item.is_alpha() || c == '-')
        },
        ErrorKind::Char,
    )
}

PS: Do you know how to tell Nom that the "-" in a slug must not be at the beginning nor the end?


Solution

  • There is nom::multi::separated_list for exactly this. And since you want the result to be string itself rather than a vector of segments, combining it with nom::combinator::recognize will do the trick:

    use std::error::Error;
    use nom::{
        IResult,
        character::complete::{alphanumeric1, char},
        combinator::recognize,
        multi::separated_list,
        sequence::delimited,
    };
    
    fn slug_parse<'a>(text: &'a str) -> IResult<&'a str, &'a str> {
        let slug = separated_list(char('-'), alphanumeric1);
        delimited(char(':'), recognize(slug), char(':'))(text)
    }
    
    fn main() -> Result<(), Box<dyn Error>> {
        let (_, res) = slug_parse(":hello-world-i-only-accept-alpha-numeric-char-and-dashes:")?;
        assert_eq!(
          res,
          "hello-world-i-only-accept-alpha-numeric-char-and-dashes"
        );
    
        Ok(())
    }