diff --git a/bin/ofs/Cargo.lock b/bin/ofs/Cargo.lock index c0ea5dfe645..72e911b6776 100644 --- a/bin/ofs/Cargo.lock +++ b/bin/ofs/Cargo.lock @@ -1153,6 +1153,16 @@ dependencies = [ "serde", ] +[[package]] +name = "quick-xml" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86e446ed58cef1bbfe847bc2fda0e2e4ea9f0e57b90c507d4781292590d72a4e" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quinn" version = "0.11.5" diff --git a/bindings/nodejs/CONTRIBUTING.md b/bindings/nodejs/CONTRIBUTING.md index 01d49424b85..d304ec88054 100644 --- a/bindings/nodejs/CONTRIBUTING.md +++ b/bindings/nodejs/CONTRIBUTING.md @@ -79,11 +79,11 @@ Contains the following tools: ```bash # Install dependencies. -> pnpm install +pnpm install # Build from source. -> pnpm build +pnpm build # Build from source with debug info. -> pnpm build:debug +pnpm build:debug ``` ## Test @@ -92,8 +92,12 @@ We are using our own developed behavior testing framework. Taking 'service-memory' as an example, you can use the following command to run it. ```bash -> OPENDAL_TEST=memory pnpm test +OPENDAL_TEST=memory pnpm test +``` + +which will output: +``` ✓ |opendal| tests/service.test.mjs (8 tests | 2 skipped) 40ms Test Files 1 passed (1) diff --git a/bindings/nodejs/Cargo.toml b/bindings/nodejs/Cargo.toml index b783dd39321..f8f7a52d155 100644 --- a/bindings/nodejs/Cargo.toml +++ b/bindings/nodejs/Cargo.toml @@ -156,12 +156,12 @@ crate-type = ["cdylib"] doc = false [dependencies] -futures = "0.3.28" -napi = { version = "2.11.3", default-features = false, features = [ +futures = "0.3.31" +napi = { version = "3.0.0-alpha.13", default-features = false, features = [ "napi6", "async", ] } -napi-derive = "2.14.6" +napi-derive = "3.0.0-alpha.13" # this crate won't be published, we always use the local version opendal = { version = ">=0", path = "../../core", features = [ "layers-blocking", diff --git a/bindings/nodejs/src/lib.rs b/bindings/nodejs/src/lib.rs index bd6c688099f..d78ea55ffef 100644 --- a/bindings/nodejs/src/lib.rs +++ b/bindings/nodejs/src/lib.rs @@ -19,7 +19,6 @@ extern crate napi_derive; use std::collections::HashMap; -use std::fmt::Display; use std::io::Read; use std::str::FromStr; use std::time::Duration; @@ -30,6 +29,8 @@ use napi::bindgen_prelude::*; mod capability; +type Result = napi::Result; + #[napi] pub struct Operator(opendal::Operator); @@ -739,7 +740,7 @@ impl BlockingReader { #[napi] pub fn read(&mut self, mut buf: Buffer) -> Result { let buf = buf.as_mut(); - let n = self.inner.read(buf).map_err(format_napi_error)?; + let n = self.inner.read(buf).map_err(format_io_error)?; Ok(n) } } @@ -755,13 +756,13 @@ pub struct Reader { impl Reader { /// # Safety /// - /// > &mut self in async napi methods should be marked as unsafe + /// `&mut self` in async napi methods should be marked as unsafe /// /// Read bytes from this reader into given buffer. #[napi] pub async unsafe fn read(&mut self, mut buf: Buffer) -> Result { let buf = buf.as_mut(); - let n = self.inner.read(buf).await.map_err(format_napi_error)?; + let n = self.inner.read(buf).await.map_err(format_io_error)?; Ok(n) } } @@ -1144,9 +1145,75 @@ impl RetryLayer { } } +#[repr(i32)] +#[non_exhaustive] +#[derive(Eq, PartialEq, Debug, Clone, Copy)] +enum ErrorCode { + Unexpected = 1001, + Unsupported = 1002, + ConfigInvalid = 1003, + NotFound = 1004, + PermissionDenied = 1005, + IsADirectory = 1006, + NotADirectory = 1007, + AlreadyExists = 1008, + RateLimited = 1009, + IsSameFile = 1010, + ConditionNotMatch = 1011, + RangeNotSatisfied = 1012, +} + +impl AsRef for ErrorCode { + fn as_ref(&self) -> &str { + match self { + ErrorCode::Unexpected => "Unexpected", + ErrorCode::Unsupported => "Unsupported", + ErrorCode::ConfigInvalid => "ConfigInvalid", + ErrorCode::NotFound => "NotFound", + ErrorCode::PermissionDenied => "PermissionDenied", + ErrorCode::IsADirectory => "IsADirectory", + ErrorCode::NotADirectory => "NotADirectory", + ErrorCode::AlreadyExists => "AlreadyExists", + ErrorCode::RateLimited => "RateLimited", + ErrorCode::IsSameFile => "IsSameFile", + ErrorCode::ConditionNotMatch => "ConditionNotMatch", + ErrorCode::RangeNotSatisfied => "RangeNotSatisfied", + } + } +} + +fn format_io_error(e: std::io::Error) -> Error { + format_napi_error( + opendal::Error::new( + match e.kind() { + std::io::ErrorKind::NotFound => opendal::ErrorKind::NotFound, + std::io::ErrorKind::PermissionDenied => opendal::ErrorKind::PermissionDenied, + _ => opendal::ErrorKind::Unexpected, + }, + "failed to read bytes", + ) + .set_source(e), + ) +} + /// Format opendal error to napi error. -/// -/// FIXME: handle error correctly. -fn format_napi_error(err: impl Display) -> Error { - Error::from_reason(format!("{}", err)) +fn format_napi_error(err: opendal::Error) -> Error { + Error::new( + match err.kind() { + opendal::ErrorKind::Unexpected => ErrorCode::Unexpected, + opendal::ErrorKind::Unsupported => ErrorCode::Unsupported, + opendal::ErrorKind::ConfigInvalid => ErrorCode::ConfigInvalid, + opendal::ErrorKind::NotFound => ErrorCode::NotFound, + opendal::ErrorKind::PermissionDenied => ErrorCode::PermissionDenied, + opendal::ErrorKind::IsADirectory => ErrorCode::IsADirectory, + opendal::ErrorKind::NotADirectory => ErrorCode::NotADirectory, + opendal::ErrorKind::AlreadyExists => ErrorCode::AlreadyExists, + opendal::ErrorKind::RateLimited => ErrorCode::RateLimited, + opendal::ErrorKind::IsSameFile => ErrorCode::IsSameFile, + opendal::ErrorKind::ConditionNotMatch => ErrorCode::ConditionNotMatch, + opendal::ErrorKind::RangeNotSatisfied => ErrorCode::RangeNotSatisfied, + kind => unimplemented!("error kind not supported: {:?}", kind), + }, + err.to_string(), + ) }