1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
use super::Lookup;
use enum_dispatch::enum_dispatch;
use std::error::Error;
use std::fmt;
use std::io::Error as IOError;

#[derive(Debug)]
pub enum CastError {
    IncompatibleCast {
        from: &'static str,
        to: &'static str,
    },
    NumParseError(<f64 as std::str::FromStr>::Err),
    RegexError(regex::Error),
}

impl Error for CastError {}
impl fmt::Display for CastError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::IncompatibleCast { from, to } => write!(f, "can't cast {} to {}", from, to),
            Self::RegexError(err) => write!(f, "couldn't parse regex: {}", err),
            Self::NumParseError(err) => write!(f, "error at parsing number: {}", err),
        }
    }
}

#[enum_dispatch]
trait Error1: Error {}

#[enum_dispatch(Error1)]
#[derive(Debug)]
pub enum ErrorKind {
    IOError,
    LookupError,
    CastError,
    ArgCountMismatched,
}

impl Error for ErrorKind {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        Some(match self {
            Self::IOError(err) => err,
            Self::LookupError(err) => err,
            Self::CastError(err) => err,
            Self::ArgCountMismatched(err) => err,
        })
    }
}

// TODO: make displays for interpreter errors have a nice
// format
impl fmt::Display for ErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.source().unwrap())
    }
}

#[derive(Debug)]
pub struct LookupError {
    lookup: Lookup,
}

impl LookupError {
    pub const fn new(lookup: Lookup) -> Self {
        Self { lookup }
    }
}

impl Error for LookupError {}
impl fmt::Display for LookupError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Lookup {} has no initializer", self.lookup)
    }
}

use crate::ast::Function;

#[derive(Debug)]
pub struct ArgCountMismatched {
    function: Function,
    got: usize,
}

impl ArgCountMismatched {
    pub const fn new(function: Function, got: usize) -> Self {
        Self { function, got }
    }

    pub const fn check(function: Function, arg_count: usize) -> Result<(), Self> {
        if arg_count < function.minimum_arg_count() as usize {
            Err(Self::new(function, arg_count))
        } else {
            Ok(())
        }
    }
}

impl Error for ArgCountMismatched {}

impl fmt::Display for ArgCountMismatched {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{function} expected at least {expected_count} arguments, but received {actual_count} instead", function = self.function, expected_count = self.function.minimum_arg_count(), actual_count = self.got)
    }
}