Skip to content

Commit

Permalink
feat(nodejs): add WriteOptions for write methods (#4785)
Browse files Browse the repository at this point in the history
* feat(nodejs): add `OpWriteOptions` properties for write methods

close: #4782

* chore

* chore

* refactor: remove trait and rename
  • Loading branch information
bxb100 authored Jun 24, 2024
1 parent 47b1759 commit 591d034
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 24 deletions.
3 changes: 3 additions & 0 deletions bindings/nodejs/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ generated.js
generated.d.ts
.yarn
pnpm-lock.yaml
.devbox
devbox.json
devbox.lock
70 changes: 66 additions & 4 deletions bindings/nodejs/generated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,64 @@ export interface ListOptions {
limit?: number
recursive?: boolean
}
export interface WriteOptions {
/**
* Append bytes into path.
*
* ### Notes
*
* - It always appends content to the end of the file.
* - It will create file if the path not exists.
*/
append?: boolean
/**
* Set the chunk of op.
*
* If chunk is set, the data will be chunked by the underlying writer.
*
* ## NOTE
*
* Service could have their own minimum chunk size while perform write
* operations like multipart uploads. So the chunk size may be larger than
* the given buffer size.
*/
chunk?: bigint
/** Set the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of op. */
contentType?: string
/** Set the [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) of op. */
contentDisposition?: string
/** Set the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) of op. */
cacheControl?: string
}
export interface WriterOptions {
/**
* Append bytes into path.
*
* ### Notes
*
* - It always appends content to the end of the file.
* - It will create file if the path not exists.
*/
append?: boolean
/**
* Set the chunk of op.
*
* If chunk is set, the data will be chunked by the underlying writer.
*
* ## NOTE
*
* Service could have their own minimum chunk size while perform write
* operations like multipart uploads. So the chunk size may be larger than
* the given buffer size.
*/
chunk?: bigint
/** Set the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of op. */
contentType?: string
/** Set the [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) of op. */
contentDisposition?: string
/** Set the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) of op. */
cacheControl?: string
}
/** PresignedRequest is a presigned request return by `presign`. */
export interface PresignedRequest {
/** HTTP method of this request. */
Expand Down Expand Up @@ -265,21 +323,23 @@ export class Operator {
* await op.write("path/to/file", Buffer.from("hello world"));
* // or
* await op.write("path/to/file", "hello world");
* // or
* await op.write("path/to/file", Buffer.from("hello world"), { contentType: "text/plain" });
* ```
*/
write(path: string, content: Buffer | string): Promise<void>
write(path: string, content: Buffer | string, options?: WriteOptions | undefined | null): Promise<void>
/**
* Write multiple bytes into path.
*
* It could be used to write large file in a streaming way.
*/
writer(path: string): Promise<Writer>
writer(path: string, options?: WriterOptions | undefined | null): Promise<Writer>
/**
* Write multiple bytes into path synchronously.
*
* It could be used to write large file in a streaming way.
*/
writerSync(path: string): BlockingWriter
writerSync(path: string, options?: WriterOptions | undefined | null): BlockingWriter
/**
* Write bytes into path synchronously.
*
Expand All @@ -288,9 +348,11 @@ export class Operator {
* op.writeSync("path/to/file", Buffer.from("hello world"));
* // or
* op.writeSync("path/to/file", "hello world");
* // or
* op.writeSync("path/to/file", Buffer.from("hello world"), { contentType: "text/plain" });
* ```
*/
writeSync(path: string, content: Buffer | string): void
writeSync(path: string, content: Buffer | string, options?: WriteOptions | undefined | null): void
/**
* Append bytes into path.
*
Expand Down
196 changes: 176 additions & 20 deletions bindings/nodejs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
#[macro_use]
extern crate napi_derive;

mod capability;

use std::collections::HashMap;
use std::fmt::Display;
use std::io::Read;
Expand All @@ -30,6 +28,8 @@ use futures::AsyncReadExt;
use futures::TryStreamExt;
use napi::bindgen_prelude::*;

mod capability;

#[napi]
pub struct Operator(opendal::Operator);

Expand Down Expand Up @@ -226,56 +226,149 @@ impl Operator {
})
}

//noinspection DuplicatedCode
/// Write bytes into path.
///
/// ### Example
/// ```javascript
/// await op.write("path/to/file", Buffer.from("hello world"));
/// // or
/// await op.write("path/to/file", "hello world");
/// // or
/// await op.write("path/to/file", Buffer.from("hello world"), { contentType: "text/plain" });
/// ```
#[napi]
pub async fn write(&self, path: String, content: Either<Buffer, String>) -> Result<()> {
pub async fn write(
&self,
path: String,
content: Either<Buffer, String>,
options: Option<WriteOptions>,
) -> Result<()> {
let c = match content {
Either::A(buf) => buf.as_ref().to_owned(),
Either::B(s) => s.into_bytes(),
};
self.0.write(&path, c).await.map_err(format_napi_error)
let mut writer = self.0.write_with(&path, c);
if let Some(options) = options {
if let Some(append) = options.append {
writer = writer.append(append);
}
if let Some(chunk) = options.chunk {
writer = writer.chunk(chunk.get_u64().1 as usize);
}
if let Some(ref content_type) = options.content_type {
writer = writer.content_type(content_type);
}
if let Some(ref content_disposition) = options.content_disposition {
writer = writer.content_disposition(content_disposition);
}
if let Some(ref cache_control) = options.cache_control {
writer = writer.cache_control(cache_control);
}
}
writer.await.map_err(format_napi_error)
}

//noinspection DuplicatedCode
/// Write multiple bytes into path.
///
/// It could be used to write large file in a streaming way.
#[napi]
pub async fn writer(&self, path: String) -> Result<Writer> {
let w = self.0.writer(&path).await.map_err(format_napi_error)?;
pub async fn writer(&self, path: String, options: Option<WriterOptions>) -> Result<Writer> {
let mut writer = self.0.writer_with(&path);
if let Some(options) = options {
if let Some(append) = options.append {
writer = writer.append(append);
}
if let Some(chunk) = options.chunk {
writer = writer.chunk(chunk.get_u64().1 as usize);
}
if let Some(ref content_type) = options.content_type {
writer = writer.content_type(content_type);
}
if let Some(ref content_disposition) = options.content_disposition {
writer = writer.content_disposition(content_disposition);
}
if let Some(ref cache_control) = options.cache_control {
writer = writer.cache_control(cache_control);
}
}
let w = writer.await.map_err(format_napi_error)?;
Ok(Writer(w))
}

/// Write multiple bytes into path synchronously.
///
/// It could be used to write large file in a streaming way.
#[napi]
pub fn writer_sync(&self, path: String) -> Result<BlockingWriter> {
let w = self.0.blocking().writer(&path).map_err(format_napi_error)?;
pub fn writer_sync(
&self,
path: String,
options: Option<WriterOptions>,
) -> Result<BlockingWriter> {
let mut writer = self.0.blocking().writer_with(&path);
if let Some(options) = options {
if let Some(append) = options.append {
writer = writer.append(append);
}
if let Some(chunk) = options.chunk {
writer = writer.buffer(chunk.get_u64().1 as usize);
}
if let Some(ref content_type) = options.content_type {
writer = writer.content_type(content_type);
}
if let Some(ref content_disposition) = options.content_disposition {
writer = writer.content_disposition(content_disposition);
}
if let Some(ref cache_control) = options.cache_control {
writer = writer.cache_control(cache_control);
}
}
let w = writer.call().map_err(format_napi_error)?;
Ok(BlockingWriter(w))
}

//noinspection DuplicatedCode
/// Write bytes into path synchronously.
///
/// ### Example
/// ```javascript
/// op.writeSync("path/to/file", Buffer.from("hello world"));
/// // or
/// op.writeSync("path/to/file", "hello world");
/// // or
/// op.writeSync("path/to/file", Buffer.from("hello world"), { contentType: "text/plain" });
/// ```
#[napi]
pub fn write_sync(&self, path: String, content: Either<Buffer, String>) -> Result<()> {
pub fn write_sync(
&self,
path: String,
content: Either<Buffer, String>,
options: Option<WriteOptions>,
) -> Result<()> {
let c = match content {
Either::A(buf) => buf.as_ref().to_owned(),
Either::B(s) => s.into_bytes(),
};
self.0.blocking().write(&path, c).map_err(format_napi_error)
let mut writer = self.0.blocking().write_with(&path, c);
if let Some(options) = options {
if let Some(append) = options.append {
writer = writer.append(append);
}
if let Some(chunk) = options.chunk {
writer = writer.chunk(chunk.get_u64().1 as usize);
}
if let Some(ref content_type) = options.content_type {
writer = writer.content_type(content_type);
}
if let Some(ref content_disposition) = options.content_disposition {
writer = writer.content_disposition(content_disposition);
}
if let Some(ref cache_control) = options.cache_control {
writer = writer.cache_control(cache_control);
}
}
writer.call().map_err(format_napi_error)
}

/// Append bytes into path.
Expand All @@ -293,16 +386,15 @@ impl Operator {
/// ```
#[napi]
pub async fn append(&self, path: String, content: Either<Buffer, String>) -> Result<()> {
let c = match content {
Either::A(buf) => buf.as_ref().to_owned(),
Either::B(s) => s.into_bytes(),
};

self.0
.write_with(&path, c)
.append(true)
.await
.map_err(format_napi_error)
self.write(
path,
content,
Some(WriteOptions {
append: Some(true),
..Default::default()
}),
)
.await
}

/// Copy file according to given `from` and `to` path.
Expand Down Expand Up @@ -790,6 +882,70 @@ impl Writer {
}
}

#[napi(object)]
#[derive(Default)]
pub struct WriteOptions {
/// Append bytes into path.
///
/// ### Notes
///
/// - It always appends content to the end of the file.
/// - It will create file if the path not exists.
pub append: Option<bool>,

/// Set the chunk of op.
///
/// If chunk is set, the data will be chunked by the underlying writer.
///
/// ## NOTE
///
/// Service could have their own minimum chunk size while perform write
/// operations like multipart uploads. So the chunk size may be larger than
/// the given buffer size.
pub chunk: Option<BigInt>,

/// Set the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of op.
pub content_type: Option<String>,

/// Set the [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) of op.
pub content_disposition: Option<String>,

/// Set the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) of op.
pub cache_control: Option<String>,
}

#[napi(object)]
#[derive(Default)]
pub struct WriterOptions {
/// Append bytes into path.
///
/// ### Notes
///
/// - It always appends content to the end of the file.
/// - It will create file if the path not exists.
pub append: Option<bool>,

/// Set the chunk of op.
///
/// If chunk is set, the data will be chunked by the underlying writer.
///
/// ## NOTE
///
/// Service could have their own minimum chunk size while perform write
/// operations like multipart uploads. So the chunk size may be larger than
/// the given buffer size.
pub chunk: Option<BigInt>,

/// Set the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of op.
pub content_type: Option<String>,

/// Set the [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) of op.
pub content_disposition: Option<String>,

/// Set the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) of op.
pub cache_control: Option<String>,
}

/// Lister is designed to list entries at given path in an asynchronous
/// manner.
#[napi]
Expand Down
Loading

0 comments on commit 591d034

Please sign in to comment.