Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V3: Added JsCallable #9813

Merged
merged 12 commits into from
Jun 21, 2024
Merged

V3: Added JsCallable #9813

merged 12 commits into from
Jun 21, 2024

Conversation

alshdavid
Copy link
Contributor

@alshdavid alshdavid commented Jun 20, 2024

Added JsCallable utility to simplify the usage of napi JavaScript functions from Rust.

These must be run in their own thread. This works with async functions, sync functions, and promises.

Please leave your thoughts and comments 🙏

Usage

#[napi]
pub fn example(callback: JsFunction) -> napi::Result<()> {
  let callable = JsCallable::new(callback)?;

  thread::spawn(move || {
    let result = callable.call_with_return_serde::<bool>(42)?;
    println!("From thread {:?}", result); // "true"
  });

  Ok(())
}

Or with the FileSystemNapi wrapper

pub struct FileSystemNapi {
  read_file_fn: JsCallable,
}

impl FileSystemNapi {
  pub fn new(js_file_system: &JsObject) -> napi::Result<Self> {
    Ok(Self {
      read_file_fn: JsCallable::new_from_object_prop("readFileSync", &js_file_system)?,
    })
  }
}

impl FileSystem for FileSystemNapi {
  fn read_to_string(&self, path: &Path) -> std::io::Result<String> {
    self
      .read_file_fn
      .call_with_return_serde((path.to_path_buf(), "utf8"))
      .map_err(|e| std::io::Error::other(e))
  }
}

Before / After hooks

The call and call_with_return methods take mapping functions to let you cast Rust types to napi types and back. This is useful if you need to supply a callback as a parameter or do some custom deserialization logic (often needed for enums)

/*
callable.call_with_return(
  map_params_fn,
  map_return_fn,
)
*/

let value_to_send = 42;

let result = callable.call_with_return(
  move |env| { 
    // Runs before the target function to send values into JS
    let v = env.create_int32(value_to_send)?.into_unknown();
    Ok(vec![v])
  },
  move |env, returned| {
    // Runs after target function to return values back
    let value = env.from_js_value::<bool, _>(returned)?;
    Ok(value)
  }
)?;

Mappers

Included are helper functions map_params_serde and map_return_serde which handle the type conversion automatically using serde

// Two params
callable.call_with_return(
  map_params_serde((42, "hello")),
  map_return_serde::<bool>(),
)

// One param
callable.call_with_return(
  map_params_serde("hello),
  map_return_serde::<bool>(),
)

// No params
callable.call_with_return(
  map_params_serde(()),
  map_return_serde::<bool>(),
)

@alshdavid alshdavid enabled auto-merge (squash) June 21, 2024 03:28
@alshdavid alshdavid merged commit 6c7ded9 into v2 Jun 21, 2024
16 of 17 checks passed
@yamadapc yamadapc deleted the alsh/js-callable branch June 21, 2024 03:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants