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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::fmt;


// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-native-error-types-used-in-this-standard
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum ErrorKind {
    SyntaxError,
    
    EvalError,
    RangeError,
    ReferenceError,
    TypeError,
    URIError,

    // non-standard
    InternalError,
}

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

        match *self {
            SyntaxError => write!(f, "SyntaxError"),
            EvalError => write!(f, "EvalError"),
            RangeError => write!(f, "RangeError"),
            ReferenceError => write!(f, "ReferenceError"),
            TypeError => write!(f, "TypeError"),
            URIError=> write!(f, "URIError"),
            InternalError => write!(f, "InternalError"),
        }
    }
}


#[derive(PartialEq, Eq, Clone)]
pub struct Error {
    kind: ErrorKind,
    message: String,
    filename: Option<String>,
    line_number: Option<usize>,
    column_number: Option<usize>,
    line: Option<String>,
}

impl Error {
    pub fn new<M: Into<String>>(kind: ErrorKind, message: M) -> Self {
        Self { 
            kind,
            message: message.into(),
            filename: None,
            line_number: None,
            column_number: None,
            line: None,
        }
    }

    pub fn kind(&self) -> ErrorKind {
        self.kind
    }

    pub fn message(&self) -> &str {
        &self.message
    }

    pub fn filename(&self) -> &str {
        match self.filename {
            None => panic!("Ensure Stack Infomation is added."),
            Some(ref filename) => &filename
        }
    }

    pub fn line_number(&self) -> usize {
        match self.line_number {
            None => panic!("Ensure Stack Infomation is added."),
            Some(line_number) => line_number
        }
    }

    pub fn column_number(&self) -> usize {
        match self.column_number {
            None => panic!("Ensure Stack Infomation is added."),
            Some(column_number) => column_number
        }
    }

    // NOTE: Set Stack infomation
    pub fn set_stack<F: Into<String>>(&mut self, 
                                      filename: F,
                                      line_number: usize,
                                      column_number: usize,
                                      line: Option<String>) {
        self.filename = Some(filename.into());
        self.line_number = Some(line_number);
        self.column_number = Some(column_number);

        if line.is_some() {
            self.line = line;
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let _ = writeln!(f, "{}: {}", self.kind, self.message);
        let _ = writeln!(f, " --> {}:{}:{}", self.filename(), self.line_number() + 1, self.column_number());

        match self.line {
            Some(ref line) => {
                write!(f, "{}", line);
            },
            None => { },
        }

        Ok(())
    }
}