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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use std::fmt;
use std::string::FromUtf8Error;
use std::str::Utf8Error;
use std::ptr;

use crate::sys::napi_status;
use crate::sys::napi_value;
use crate::val::JsEnv;
use crate::IntoJs;

#[derive(Debug)]
pub enum NjError {
    NapiCall(NapiStatus),
    InvalidArgCount(usize, usize),
    InvalidArgIndex(usize, usize),
    InvalidType(String, String),
    NoPlainConstructor,
    Utf8Error(FromUtf8Error),
    Utf8ErrorSlice(Utf8Error),
    Native(napi_value),
    Other(String),
}

// errors are thrown
impl IntoJs for NjError {
    fn into_js(self, js_env: &JsEnv) -> napi_value {
        match self {
            NjError::Native(err) => {
                js_env.throw(err);
                ptr::null_mut()
            }
            _ => {
                let msg = self.to_string();
                js_env.throw_type_error(&msg);
                ptr::null_mut()
            }
        }
    }
}

impl NjError {
    /// convert to napi value
    pub fn as_js(&self, js_env: &JsEnv) -> napi_value {
        match self {
            NjError::Native(err) => *err,
            _ => {
                let msg = self.to_string();
                js_env.create_error(&msg).expect("error cannot be created")
            }
        }
    }
}

impl IntoJs for Result<napi_value, NjError> {
    fn into_js(self, js_env: &JsEnv) -> napi_value {
        match self {
            Ok(napi_val) => napi_val,
            Err(err) => err.into_js(js_env),
        }
    }
}

impl From<FromUtf8Error> for NjError {
    fn from(error: FromUtf8Error) -> Self {
        Self::Utf8Error(error)
    }
}

impl From<Utf8Error> for NjError {
    fn from(error: Utf8Error) -> Self {
        Self::Utf8ErrorSlice(error)
    }
}

impl From<NapiStatus> for NjError {
    fn from(status: NapiStatus) -> Self {
        Self::NapiCall(status)
    }
}

impl fmt::Display for NjError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::NapiCall(status) => write!(f, "napi call failed {status:#?}"),
            Self::InvalidType(expected, actual) => {
                write!(f, "invalid type, expected: {expected}, actual: {actual}")
            }
            Self::Utf8Error(err) => write!(f, "ut8 error: {err}"),
            Self::Utf8ErrorSlice(err) => write!(f, "ut8 error: {err}"),
            Self::InvalidArgIndex(index, len) => {
                write!(f, "attempt to access arg: {index} out of len: {len}")
            }
            Self::InvalidArgCount(actual_count, expected_count) => write!(
                f,
                "{expected_count} args expected but {actual_count} is present"
            ),
            Self::NoPlainConstructor => write!(f, "Plain constructor not supported yet"),
            Self::Native(_val) => write!(f, "Native error payload"),
            Self::Other(msg) => write!(f, "{msg}"),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub enum NapiStatus {
    Ok = crate::sys::napi_status_napi_ok as isize,
    InvalidArg = crate::sys::napi_status_napi_invalid_arg as isize,
    ObjectExpected = crate::sys::napi_status_napi_object_expected as isize,
    StringExpected = crate::sys::napi_status_napi_string_expected as isize,
    NameExpected = crate::sys::napi_status_napi_name_expected as isize,
    FunctionExpected = crate::sys::napi_status_napi_function_expected as isize,
    NumberExpected = crate::sys::napi_status_napi_number_expected as isize,
    BooleanExpected = crate::sys::napi_status_napi_boolean_expected as isize,
    ArrayExpected = crate::sys::napi_status_napi_array_expected as isize,
    GenericFailure = crate::sys::napi_status_napi_generic_failure as isize,
    PendingException = crate::sys::napi_status_napi_pending_exception as isize,
    Cancelled = crate::sys::napi_status_napi_cancelled as isize,
    EscapeCalledTwice = crate::sys::napi_status_napi_escape_called_twice as isize,
    HandleScopeMismatch = crate::sys::napi_status_napi_handle_scope_mismatch as isize,
    CallbackScopeMismatch = crate::sys::napi_status_napi_callback_scope_mismatch as isize,
    QueueFull = crate::sys::napi_status_napi_queue_full as isize,
    Closing = crate::sys::napi_status_napi_closing as isize,
    BigintExpected = crate::sys::napi_status_napi_bigint_expected as isize,
    DateExpected = crate::sys::napi_status_napi_date_expected as isize,
    ArraybufferExpected = crate::sys::napi_status_napi_arraybuffer_expected as isize,
    DetachableArrayBufferExpected =
        crate::sys::napi_status_napi_detachable_arraybuffer_expected as isize,
}

impl From<napi_status> for NapiStatus {
    fn from(status: napi_status) -> Self {
        match status {
            crate::sys::napi_status_napi_ok => Self::Ok,
            crate::sys::napi_status_napi_invalid_arg => Self::InvalidArg,
            crate::sys::napi_status_napi_object_expected => Self::ObjectExpected,
            crate::sys::napi_status_napi_string_expected => Self::StringExpected,
            crate::sys::napi_status_napi_name_expected => Self::NameExpected,
            crate::sys::napi_status_napi_function_expected => Self::FunctionExpected,
            crate::sys::napi_status_napi_number_expected => Self::NumberExpected,
            crate::sys::napi_status_napi_boolean_expected => Self::BooleanExpected,
            crate::sys::napi_status_napi_array_expected => Self::ArrayExpected,
            crate::sys::napi_status_napi_generic_failure => Self::GenericFailure,
            crate::sys::napi_status_napi_pending_exception => Self::PendingException,
            crate::sys::napi_status_napi_cancelled => Self::Cancelled,
            crate::sys::napi_status_napi_escape_called_twice => Self::EscapeCalledTwice,
            crate::sys::napi_status_napi_handle_scope_mismatch => Self::HandleScopeMismatch,
            crate::sys::napi_status_napi_callback_scope_mismatch => Self::CallbackScopeMismatch,
            crate::sys::napi_status_napi_queue_full => Self::QueueFull,
            crate::sys::napi_status_napi_closing => Self::Closing,
            crate::sys::napi_status_napi_bigint_expected => Self::BigintExpected,
            crate::sys::napi_status_napi_date_expected => Self::DateExpected,
            crate::sys::napi_status_napi_arraybuffer_expected => Self::ArraybufferExpected,
            crate::sys::napi_status_napi_detachable_arraybuffer_expected => {
                Self::DetachableArrayBufferExpected
            }
            _ => panic!("cannot convert: {}", status),
        }
    }
}