Skip to content

Instantly share code, notes, and snippets.

@Brooooooklyn
Last active January 16, 2024 16:42
Show Gist options
  • Save Brooooooklyn/f8d985e4a93fdd16e975170632dee0ee to your computer and use it in GitHub Desktop.
Save Brooooooklyn/f8d985e4a93fdd16e975170632dee0ee to your computer and use it in GitHub Desktop.
fetch implementation using NAPI-RS and reqwest
fn main() {
napi_build::setup();
}
[package]
name = "napi-rs_fetch"
version = "1.0.0"
edition = "2021"
license = "MIT"
[lib]
crate-type = ["cdylib"]
[dependencies]
anyhow = "1"
reqwest = { version = "0.11", features = ["json"] }
serde_json = "1"
[dependencies.napi]
version = "2"
default-features = false
# see https://nodejs.org/api/n-api.html#node-api-version-matrix
features = ["anyhow", "async", "serde-json"]
[dependencies.napi-derive]
version = "2"
features = ["type-def"]
[build-dependencies]
napi-build = "2"
[profile.release]
lto = true
use std::collections::HashMap;
use napi::bindgen_prelude::*;
use napi_derive::napi;
#[napi(object)]
pub struct RequestInit {
pub method: Option<String>,
pub headers: Option<HashMap<String, String>>,
}
#[napi]
pub async fn fetch(input: String, init: Option<RequestInit>) -> Result<Response> {
let client = reqwest::ClientBuilder::default()
.build()
.map_err(anyhow::Error::from)?;
let inner = {
match init
.as_ref()
.and_then(|i| i.method.as_ref())
.map(|m| m.as_str())
{
Some("post") => client.post(&input),
Some("put") => client.put(&input),
Some("delete") => client.delete(&input),
Some("head") => client.head(&input),
Some("patch") => client.patch(&input),
Some("get") | None => client.get(&input),
Some(method) => {
return Err(anyhow::anyhow!("Unsupported method: {}", method).into());
}
}
}
.headers(
init.map(|i| {
i.headers
.unwrap_or_default()
.into_iter()
.map(|(k, v)| (k.parse().unwrap(), v.parse().unwrap()))
.collect()
})
.unwrap_or_default(),
)
.send()
.await
.map_err(anyhow::Error::from)?;
Ok(Response { inner: Some(inner) })
}
#[napi]
pub struct Response {
inner: Option<reqwest::Response>,
}
#[napi]
impl Response {
#[inline]
fn checked(&mut self) -> Result<reqwest::Response> {
self.inner
.take()
.ok_or_else(|| anyhow::anyhow!("Response already used").into())
}
#[napi]
pub async unsafe fn text(&mut self) -> Result<String> {
Ok(self.checked()?.text().await.map_err(anyhow::Error::from)?)
}
#[napi]
pub async unsafe fn json(&mut self) -> Result<serde_json::Value> {
Ok(self.checked()?.json().await.map_err(anyhow::Error::from)?)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment