trivet 3.1.0

The trivet Parser Library
Documentation
// Trivet
// Copyright (c) 2025 by Stacy Prowell.  All rights reserved.
// https://gitlab.com/binary-tools/trivet

//! Define the parse errors that can be reported.

use super::loc::Loc;
use std::error;
use std::fmt;

/// A result type appropriate for the parser.
pub type ParseResult<A> = Result<A, ParseError>;

/// An error type to indicate a parse error.
///
/// To use this just create an instance of the appropriate error.
/// Typically in a method returning a `Result` you will want to
/// wrap the error with `Err(...)`, and may want to force an
/// immediate return by suffixing the whole thing with a question
/// mark (`?`).
///
#[derive(Debug)]
pub enum ParseError {
    /// An internal error occurred.
    Internal {
        /// Location in parse.
        loc: Loc,

        /// Message to the user.  This is needed to provide some sort of
        /// useful information about what happened.
        msg: String,
    },

    /// The item to be parsed is not implemented.
    Unimplemented {
        /// Location in parse.
        loc: Loc,

        /// The name of the thing that is not implemented.
        thing: String,
    },

    /// An I/O error occurred reading from the stream.
    Io {
        /// Location in parse.
        loc: Loc,

        /// Underlying I/O error.
        cause: std::io::Error,
    },

    /// A syntax error was detected.
    Syntax {
        /// Location in parse.
        loc: Loc,

        /// Message to the user describing the error.
        msg: String,
    },

    /// Something was read that was not expected at this point.
    Unexpected {
        /// Location in parse.
        loc: Loc,

        /// Human-readable description of the thing that was expected.
        expected: String,

        /// Human-readable description of the thing that was found.
        found: String,
    },

    /// An underlying error occurred.
    Error {
        /// Location in parse.
        loc: Loc,

        /// The underying error.
        cause: Box<dyn std::error::Error>,
    },

    /// The parser expected to find something, but that thing was not found.
    NotFound {
        /// Location in parse.
        loc: Loc,

        /// The thing that was not found.
        what: String,
    },
}

impl ParseError {
    /// Get the location of this error.
    pub fn loc(&self) -> &Loc {
        match self {
            ParseError::Error { loc, .. } => loc,
            ParseError::Internal { loc, .. } => loc,
            ParseError::Io { loc, .. } => loc,
            ParseError::NotFound { loc, .. } => loc,
            ParseError::Syntax { loc, .. } => loc,
            ParseError::Unexpected { loc, .. } => loc,
            ParseError::Unimplemented { loc, .. } => loc,
        }
    }
}

impl error::Error for ParseError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match *self {
            Self::Io { ref cause, .. } => Some(cause),
            _ => None,
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, form: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Internal { loc, msg } => {
                write!(form, "{}: Internal Error: {}", loc, msg)
            }
            Self::Unimplemented { loc, thing } => {
                write!(form, "{}: Not Implemented: {}", loc, thing)
            }
            Self::Io { loc, cause } => {
                write!(form, "{}: I/O Error: {}", loc, cause)
            }
            Self::Syntax { loc, msg } => {
                write!(form, "{}: Syntax Error: {}", loc, msg)
            }
            Self::Unexpected {
                loc,
                expected,
                found,
            } => {
                write!(form, "{}: Expected {}, but found {}", loc, expected, found)
            }
            Self::Error { loc, cause: err } => {
                write!(form, "{}: {}", loc, err)
            }
            Self::NotFound { loc, what } => {
                write!(form, "{}: Could not find {}", loc, what)
            }
        }
    }
}

/// Get an unexpected end of file parse error.
pub fn unexpected_eof_error(loc: Loc) -> ParseError {
    let ioe = std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "Unexpected end of file");
    ParseError::Io { loc, cause: ioe }
}

/// Get a parse error with the specified message.
pub fn syntax_error(loc: Loc, msg: &str) -> ParseError {
    ParseError::Syntax {
        msg: msg.to_string(),
        loc,
    }
}

/// Get a parse error that indicates we expected to find one thing, but found something else.
pub fn unexpected_text_error(loc: Loc, expected: &str, found: &str) -> ParseError {
    ParseError::Unexpected {
        loc,
        expected: expected.to_owned(),
        found: found.to_owned(),
    }
}

/// Get a parse error with information about an unexpected character.
pub fn unexpected_character_error(loc: Loc, expected: &str, found: char) -> ParseError {
    ParseError::Unexpected {
        loc,
        expected: expected.to_owned(),
        found: format!("'{}'", found),
    }
}

/// Get an internal error.
pub fn internal_error(loc: Loc, msg: &str) -> ParseError {
    ParseError::Internal {
        msg: msg.to_owned(),
        loc,
    }
}

/// Something mentioned in the parse could not be found.
pub fn not_found_error(loc: Loc, what: &str) -> ParseError {
    ParseError::NotFound {
        loc,
        what: what.to_owned(),
    }
}

/// An I/O error occurred.
pub fn io_error(loc: Loc, cause: std::io::Error) -> ParseError {
    ParseError::Io { loc, cause }
}

/// Something was referenced but it is not yet implemented.
pub fn unimplemented_error(loc: Loc, thing: &str) -> ParseError {
    ParseError::Unimplemented {
        loc,
        thing: thing.to_owned(),
    }
}

/// Wrap another error as a parse error.
pub fn error<E>(loc: Loc, err: E) -> ParseError
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    // We need to re-package the error as an I/O error.
    let err = std::io::Error::new(std::io::ErrorKind::Other, err);
    ParseError::Error {
        loc,
        cause: Box::new(err),
    }
}