inkling 0.12.5

Limited implementation of the Ink markup language.
Documentation
//! Errors from variable assignments or operations.

use std::{error::Error, fmt};

use crate::line::Variable;

use std::cmp::Ordering;

impl Error for VariableError {}

#[derive(Clone, Debug)]
/// Error from invalid variable assignments or operations.
pub struct VariableError {
    /// Variable that caused or detected the error.
    pub variable: Variable,
    /// Error variant.
    pub kind: VariableErrorKind,
}

impl VariableError {
    pub(crate) fn from_kind<T: Into<Variable>>(variable: T, kind: VariableErrorKind) -> Self {
        VariableError {
            variable: variable.into(),
            kind,
        }
    }
}

#[derive(Clone, Debug)]
/// Error variant for variable type errors.
pub enum VariableErrorKind {
    /// Divided with or took the remainer from 0.
    DividedByZero {
        /// Zero-valued variable in the operation.
        other: Variable,
        /// Character representation of the operation that caused the error (`/`, `%`).
        operator: char,
    },
    /// Two variables could not be compared to each other like this.
    InvalidComparison {
        /// Other variable in the comparison.
        other: Variable,
        /// Type of comparison betweeen `variable` and `other`.
        comparison: Ordering,
    },
    /// Tried to operate on the variable with an operation that is not allowed for it.
    InvalidOperation {
        /// Other variable in the operation.
        other: Variable,
        /// Character representation of operation (`+`, `-`, `*`, `/`, `%`).
        operator: char,
    },
    /// A new variable type was attempted to be assigned to the current variable.
    NonMatchingAssignment {
        /// Variable that was to be assigned but has non-matching type.
        other: Variable,
    },
}

impl fmt::Display for VariableError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use VariableErrorKind::*;

        let variable = &self.variable;

        match &self.kind {
            DividedByZero { other, operator } => write!(
                f,
                "Attempted to divide by 0 in the operation '{} {} {}'",
                variable.to_string_simple(),
                operator,
                other.to_string_simple()
            ),
            InvalidComparison { other, comparison } => {
                let operator = match comparison {
                    Ordering::Equal => "==",
                    Ordering::Less => ">",
                    Ordering::Greater => "<",
                };

                write!(
                    f,
                    "Cannot compare variable of type '{}' to '{}' using the '{op}' operator \
                     (in: '{} {op} {}')",
                    variable.variant_string(),
                    other.variant_string(),
                    variable.to_string_simple(),
                    other.to_string_simple(),
                    op = operator
                )
            }
            InvalidOperation { other, operator } => write!(
                f,
                "Operation '{op}' is not allowed between variables of type '{}' and '{}' \
                 (in: '{} {op} {}')",
                variable.variant_string(),
                other.variant_string(),
                variable.to_string_simple(),
                other.to_string_simple(),
                op = operator
            ),
            NonMatchingAssignment { other } => write!(
                f,
                "Cannot assign a value of type '{}' to a variable of type '{}' \
                 (variables cannot change type)",
                other.variant_string(),
                variable.variant_string()
            ),
        }
    }
}