node-bindgen icon indicating copy to clipboard operation
node-bindgen copied to clipboard

Producing Errors for JS in Class Constructor

Open NovaLogicDev opened this issue 3 years ago • 7 comments

trivially

struct MyClass {
  a_string: String
}

#[node_bindgen]
impl MyClass {
  #[node_bindgen(constructor)]
  fn new() -> Self {
    Self {
      a_string: function_that_returns_result().unwrap()
    }
  }
}

This example causes the JS application to crash completely if the function returns an error, which would be great if that's what I wanted, but instead, I would like to be able to have the constructor error out, producing an error to be caught by JS instead of returning Self. Now I'm newish to rust so maybe I missed something but the only way I understand to get an error up the call stack is to keep returning Results, however #[node_bindgen(constructor)] only accepts a return of "Self." Understandably it could be possible to make a_string type Result, however, I would have to check it in every function and that simply causes problems for certain more complex objects that I intend to use.

So I am here to ask, how do I throw errors for catching in JS from my struct "constructor"?

NovaLogicDev avatar Mar 23 '21 16:03 NovaLogicDev

There are 2 approaches I can think of: First approach is have manually call N-API to create error like below:

struct ClassWithError {
    val: i32
  }
  
#[node_bindgen]
impl ClassWithError {
    #[node_bindgen(constructor)]
    fn new(env: node_bindgen::core::val::JsEnv, val_str: String) -> Self {

        match val_str.parse::<i32>() {
            Ok(val) => {
                Self {
                    val: 22
                }
            },
            Err(err) => {
                env.create_error(&format!("parse error: {}",err));
                Self {
                    val: 0
                }
            }
        }
        
    }
}

However, we need to fix procedure macro to accept JsEnv like normal method.

Second approach is allow constructor to return Result and have procedure macro to generate Error conversion.

struct ClassWithError {
    val: i32
  }
  
#[node_bindgen]
impl ClassWithError {
    #[node_bindgen(constructor)]
    fn new(val_str: String) -> Result<Self,std::num::ParseIntError> {           
                OK(Self {
                    val: val_str.parse()?
                })
     }
   
}

sehz avatar Mar 25 '21 00:03 sehz

So the second option seems like the better of the two in my case. This is because it seems that in the event of an error, the construction would produce the Error result instead of an instance with some arbitrary "null-like" value, which wouldn't be clean to implement in a case where you had some, say complex object in the struct like say a File or something.

So then my further question because I'm not exactly sure what you mean, what would the procedure macro to "generate Error conversion" look like? what would its objective be? And what would be a good resource for me to understand how that would be done, like I said I'm somewhat new at rust?

NovaLogicDev avatar Mar 25 '21 03:03 NovaLogicDev

I think https://github.com/infinyon/node-bindgen/pull/155 simplifies the issues @NovaLogicDev has had - now structs and enums to-js conversions can be autoderived. Also structured errors can be returned from Result, so something like

#[node_bindgen]
enum MyErrorType {
  Constructor,
  SomethingElse
}

#[node_bindgen]
impl ClassWithError {
    #[node_bindgen(constructor)]
    fn new(val_str: String) -> Result<Self, MyErrorType> {           
                Err(MyErrorType::Constructor)
     }
   
}

should work and throw "Constructor" once v5 is merged and released.

mkawalec avatar May 18 '21 16:05 mkawalec

@sehz is this supposed to work now with v5? I mean is a constructor supported that returns a Result<Self, ...>?

marcmo avatar Mar 09 '23 11:03 marcmo

@marcmo current release is 5.1.0. So should work. please check

sehz avatar Mar 21 '23 18:03 sehz

This doesn't work on the latest release

error[E0308]: mismatched types
   --> src/node.rs:199:1
    |
199 | #[node_bindgen]
    | ^^^^^^^^^^^^^^^ expected `IrohNode`, found `Result<IrohNode, IrohError>`
    |
    = note: expected struct `node::IrohNode`
                 found enum `Result<node::IrohNode, IrohError>`

with the constructor being defined as

#[node_bindgen]
impl IrohNode {
    #[node_bindgen(constructor)]
    pub fn new_js() -> Result<Self, Error> {
       // .. actual logic
    }
}

dignifiedquire avatar Aug 25 '23 10:08 dignifiedquire

@ozgrakkurt triage

sehz avatar Aug 30 '23 18:08 sehz