nbf 1.1.0

Draft implementation of the Nested Blocks Format, which is a human-friendly text format for expressing nested or hierarchical data.
Documentation
//! Module containing the error definitions of the parser.

use std::error::Error;
use std::fmt::{
    Display,
    Formatter,
    Result,
};

/// A parsing error which indicates its line of origin in the text as well as
/// the type of error.
#[ derive( Debug ) ]
pub struct ParsingError<ExternalErrorType>
where
    ExternalErrorType: Display + Error {

    line: usize,
    keyword: ParsingErrorType<ExternalErrorType>
}

impl<ExternalErrorType> ParsingError<ExternalErrorType>
where
    ExternalErrorType: Display + Error {

    pub fn new(
        line: usize,
        keyword: ParsingErrorType<ExternalErrorType>
    ) -> ParsingError<ExternalErrorType> {
        ParsingError {
            line,
            keyword
        }
    }
}

impl<ExternalErrorType> Error for ParsingError<ExternalErrorType>
where
    ExternalErrorType: Display + Error + 'static {

    fn source( &self ) -> Option< &( dyn Error + 'static ) > {
        if let ParsingErrorType::External( external_error ) = &self.keyword {
            Some( external_error )
        } else {
            None
        }
    }
}

/// Allows for simple printing of error information.
impl<ExternalErrorType> Display for ParsingError<ExternalErrorType>
where
    ExternalErrorType: Display + Error {

    fn fmt( &self, f: &mut Formatter<'_> ) -> Result {
        write!( f,
            "Error in line {}: {}",
            // Convert zero-based line to line counting common in text editors.
            self.line + 1,
            self.keyword,
        )
    }
}

/// The type of a parsing error.
#[ derive( Debug ) ]
pub enum ParsingErrorType<ExternalErrorType>
where
    ExternalErrorType: Display + Error {

    /// Only one kind of curly brakets is allowed per line, which has been
    /// violated when this variant occurs.
    BothBraketsInLine,
    /// A closing braket has been found where it was not expected.
    IsolatedClosingBraket,
    /// A colon is missing between type and name of an element.
    MissingColon,
    /// Type unknown to its parent.
    UnknownKeyword( String ),
    /// Missing child keyword.
    MissingChild( String ),
    /// Failed to parse a value.
    FailedParsingValue,
    /// A custom type for handling parsing errors.
    External( ExternalErrorType ),
}

/// Allows for simple printing of error information.
impl<ExternalErrorType> Display for ParsingErrorType<ExternalErrorType>
where
    ExternalErrorType: Display + Error {

    fn fmt( &self, f: &mut Formatter<'_> ) -> Result {
        
        if let ParsingErrorType::External( custom_error ) = self {
            Display::fmt( custom_error, f )?
        }

        write!( f, "{}",
            match self {
                ParsingErrorType::BothBraketsInLine =>
                    "Only an opening or a closing braket is allowed in a single line."
                        .to_owned(),
                ParsingErrorType::IsolatedClosingBraket =>
                    "Found an orphaned closing braket, for which no opening braket exists."
                        .to_owned(),
                ParsingErrorType::MissingColon =>
                    "A colon between the keyword and value is missing."
                        .to_owned(),
                ParsingErrorType::UnknownKeyword( keyword ) =>
                    format!(
                        "Unknown keyword: \"{}\"",
                        keyword,
                    ),
                ParsingErrorType::MissingChild( keyword ) =>
                    format!(
                        "The keyword \"{}\" is missing.",
                        keyword,
                    ),
                ParsingErrorType::FailedParsingValue =>
                    "Failed to parse the given value."
                        .to_owned(),
                // Handled above already, because writing to the already mutably
                // borrowed formatter `f` here is not possible.
                ParsingErrorType::External( _ ) =>
                    "".to_owned(),
            }
        )
    }
}