bfy 0.1.2

Brainfu*k interpreter and REPL written in Rust
use std::fmt::{Debug, Display, Formatter};

#[derive(PartialEq)]
pub struct InterpreterError {
    message: String,
    pub code: i32,
}

impl InterpreterError {
    pub fn new(message: String, code: i32) -> Self {
        Self {
            message: message.to_string(),
            code,
        }
    }
}

impl Display for InterpreterError {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl Debug for InterpreterError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}, code: {}", self.message, self.code)
    }
}

impl std::error::Error for InterpreterError {
    fn description(&self) -> &str {
        &self.message
    }
}

pub enum InterpreterErrorKind {
    PointerOutOfBounds(usize), // takes pointer value
    ValueOutOfBounds,
    IoError(std::io::Error),
    FlushError(std::io::Error),
    UnmatchedBracket,
    InvalidUtf8,
}

impl InterpreterErrorKind {
    pub fn to_error(&self) -> InterpreterError {
        InterpreterError::new(self.to_string(), self.code())
    }

    fn code(&self) -> i32 {
        match self {
            InterpreterErrorKind::PointerOutOfBounds(_) => 11,
            InterpreterErrorKind::ValueOutOfBounds => 12,
            InterpreterErrorKind::IoError(_) => 13,
            InterpreterErrorKind::FlushError(_) => 14,
            InterpreterErrorKind::UnmatchedBracket => 15,
            InterpreterErrorKind::InvalidUtf8 => 16,
        }
    }
}

impl Display for InterpreterErrorKind {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        match self {
            InterpreterErrorKind::PointerOutOfBounds(pointer) => {
                write!(f, "Pointer out of bounds {}", pointer)
            }
            InterpreterErrorKind::ValueOutOfBounds => write!(f, "Value out of bounds"),
            InterpreterErrorKind::IoError(error) => write!(
                f,
                "Failed to read byte from stdin: no bytes available: {}",
                error
            ),
            InterpreterErrorKind::FlushError(e) => write!(f, "Failed to flush stdout: {}", e),
            InterpreterErrorKind::UnmatchedBracket => write!(f, "Unmatched bracket"),
            InterpreterErrorKind::InvalidUtf8 => write!(f, "Invalid utf8"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions::assert_eq; // for testing only

    #[test]
    fn test_error_kind_display() {
        let error = InterpreterErrorKind::PointerOutOfBounds(10).to_error();
        assert_eq!(error.to_string(), "Pointer out of bounds 10");
        assert_eq!(error.code, 11);

        let error = InterpreterErrorKind::ValueOutOfBounds.to_error();
        assert_eq!(error.to_string(), "Value out of bounds");
        assert_eq!(error.code, 12);

        let error =
            InterpreterErrorKind::IoError(std::io::Error::new(std::io::ErrorKind::Other, "test"))
                .to_error();
        assert_eq!(
            error.to_string(),
            "Failed to read byte from stdin: no bytes available: test"
        );
        assert_eq!(error.code, 13);

        let error = InterpreterErrorKind::UnmatchedBracket.to_error();
        assert_eq!(error.to_string(), "Unmatched bracket");
        assert_eq!(error.code, 15);

        let error = InterpreterErrorKind::InvalidUtf8.to_error();
        assert_eq!(error.to_string(), "Invalid utf8");
        assert_eq!(error.code, 16);
    }

    #[test]
    fn test_error_display() {
        let error = InterpreterError::new("test".to_string(), 10);
        assert_eq!(error.to_string(), "test");
        assert_eq!(error.code, 10);
    }

    #[test]
    fn test_error_debug() {
        let error = InterpreterError::new("test".to_string(), 10);
        assert_eq!(format!("{:?}", error), "test, code: 10");
    }
}