0

I am trying to parse using nom with nom_locate, following this tutorial. I only want output in the format:

Error: line 5, column 1

But currently I get:

Error: Parsing Failure: ParseError { span: LocatedSpan { offset: 37, line: 5, fragment: "{{G]\nK(1)key [J]\n", extra: () }, message: "Error: line 5, column 1" }

Relevant code:


pub type Span<'a> = LocatedSpan<&'a str>;
pub type IResult<'a, O> = nom::IResult<Span<'a>, O, ParseError<'a>>;


#[derive(Debug, PartialEq)]
pub struct ParseError<'a> {
    span: Span<'a>,
    message: String,
}

impl<'a> ParseError<'a> {
    pub fn new(message: String, span: Span<'a>) -> Self {
        Self {
            span,
            message,
        }
    }
}

impl<'a> nom::error::ParseError<Span<'a>> for ParseError<'a> {
    fn from_error_kind(input: Span<'a>, _: nom::error::ErrorKind) -> Self {
        Self::new(format!("Error: line {}, column {}", input.location_line(), input.location_line()), input)
    }

    fn append(input: Span<'a>, _: nom::error::ErrorKind, _: Self) -> Self {
        Self::new(format!("Error: line {}, column {}", input.location_line(), input.location_line()), input)
    }

    fn from_char(input: Span<'a>, _: char) -> Self {
        Self::new(format!("Error: line {}, column {}", input.location_line(), input.location_line()), input)
    }
}

pub fn job(s: Span) -> IResult<Vec<entities::Step>> {
    let (s, steps) = many1(job_lines)(s)?;
    Ok((s, steps))
}

fn job_lines(s: Span) -> IResult<entities::Step> {
    let (s, name) = match step_name(s) {
        Ok((s, name)) => (s, name),
        Err(nom::Err::Error(_)) => {
            let line = s.location_line();
            let column = s.get_column();
            return Err(nom::Err::Failure(ParseError::new(
                format!("Error: line {}, column {}", line, column),
                s,
            )));
        },
        Err(e) => return Err(e),
    };

    Ok((s, name))
}

Relevant error code in main:

    let steps = match input::parsers::job(input::parsers::Span::new(&input)) {
        Ok((_, steps)) => steps,
        Err(error) => {
            eprintln!("{}", error);
            std::process::exit(1)
        }
    };

What do I need to do to get the short error message instead of the extensive one?

EDIT: I deleted the span property from the custom ParseError and then the Error output becomes:

Parsing Failure: ParseError { message: "Error: line 5, column 1" }

Much better, but question remains: can I get of everything except of the message itself?

Herohtar
  • 5,347
  • 4
  • 31
  • 41
musicformellons
  • 12,283
  • 4
  • 51
  • 86
  • Hard to say without seeing the code that prints the error, but I'd say: **1.** handle the error case manually instead of using `unwrap` and **2.** print `error.message` instead of printing the whole `error`. – Jmb Feb 28 '22 at 07:42
  • @Jmb you're right, I added the error code in main. I tried `error.message`, but it does not exist. I think that's because the error is from `Err(nom::Err::Failure(ParseError::new(`. So it is kind of nested or something. – musicformellons Feb 28 '22 at 07:58

1 Answers1

1

As you surmised, error is a nom::Err::Err which is an enum, so you will need to match on that to get at the inner error:

    let steps = match input::parsers::job(input::parsers::Span::new(&input)) {
        Ok((_, steps)) => steps,
        Err(nom::Err::Failure (error)) => {
            eprintln!("{}", error.message);
            std::process::exit(1)
        },
        Err(error) => {
            eprintln!("Unknown error: {}", error);
            std::process::exit(1)
        },
    };
Jmb
  • 18,893
  • 2
  • 28
  • 55