This is an automated email from the ASF dual-hosted git repository.

yuchanns pushed a commit to branch feat-c-binding-writer
in repository https://gitbox.apache.org/repos/asf/opendal.git

commit af2ce27a4344538f6711f0613b5c5d35b5b266e7
Author: Hanchin Hsieh <[email protected]>
AuthorDate: Tue Sep 24 18:11:38 2024 +0800

    feat(bindings/c): add writer operation
    
    Signed-off-by: Hanchin Hsieh <[email protected]>
---
 bindings/c/include/opendal.h                | 106 ++++++++++++++++++++++++++++
 bindings/c/src/lib.rs                       |   5 ++
 bindings/c/src/operator.rs                  |  63 +++++++++++++++++
 bindings/c/src/result.rs                    |  22 ++++++
 bindings/c/src/writer.rs                    |  60 ++++++++++++++++
 bindings/c/tests/bdd.cpp                    |  21 ++++--
 core/src/types/blocking_write/std_writer.rs |   5 +-
 7 files changed, 271 insertions(+), 11 deletions(-)

diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index 585d0be023..7da64ec677 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -216,6 +216,18 @@ typedef struct OperatorInfo OperatorInfo;
  */
 typedef struct StdReader StdReader;
 
+/**
+ * StdWriter is the adapter of [`std::io::Write`] for [`BlockingWriter`].
+ *
+ * Users can use this adapter in cases where they need to use 
[`std::io::Write`] related trait.
+ *
+ * # Notes
+ *
+ * Files are automatically closed when they go out of scope. Errors detected 
on closing are ignored
+ * by the implementation of Drop. Use the method `close` if these errors must 
be manually handled.
+ */
+typedef struct StdWriter StdWriter;
+
 /**
  * \brief opendal_bytes carries raw-bytes with its length
  *
@@ -430,6 +442,31 @@ typedef struct opendal_result_operator_reader {
   struct opendal_error *error;
 } opendal_result_operator_reader;
 
+/**
+ * \brief The result type returned by opendal's writer operation.
+ * \note The opendal_writer actually owns a pointer to
+ * a opendal::BlockingWriter, which is inside the Rust core code.
+ */
+typedef struct opendal_writer {
+  struct StdWriter *inner;
+} opendal_writer;
+
+/**
+ * \brief The result type returned by opendal_operator_writer().
+ * The result type for opendal_operator_writer(), the field `writer` contains 
the writer
+ * of the path, which is an iterator of the objects under the path. the field 
`code` represents
+ */
+typedef struct opendal_result_operator_writer {
+  /**
+   * The pointer for opendal_writer
+   */
+  struct opendal_writer *writer;
+  /**
+   * The error, if ok, it is null
+   */
+  struct opendal_error *error;
+} opendal_result_operator_writer;
+
 /**
  * \brief The result type returned by opendal_operator_is_exist().
  *
@@ -680,6 +717,22 @@ typedef struct opendal_result_reader_read {
   struct opendal_error *error;
 } opendal_result_reader_read;
 
+/**
+ * \brief The result type returned by opendal_writer_write().
+ * The result type contains a size field, which is the size of the data 
written,
+ * which is zero on error. The error field is the error code and error message.
+ */
+typedef struct opendal_result_writer_write {
+  /**
+   * The write size if succeed.
+   */
+  uintptr_t size;
+  /**
+   * The error, if ok, it is null
+   */
+  struct opendal_error *error;
+} opendal_result_writer_write;
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -970,6 +1023,47 @@ struct opendal_result_read opendal_operator_read(const 
struct opendal_operator *
 struct opendal_result_operator_reader opendal_operator_reader(const struct 
opendal_operator *op,
                                                               const char 
*path);
 
+/**
+ * \brief Blockingly create a writer for the specified path.
+ *
+ * This function prepares a writer that can be used to write data to the 
specified path
+ * using the provided operator. If successful, it returns a valid writer; 
otherwise, it
+ * returns an error.
+ *
+ * @param op The opendal_operator created previously
+ * @param path The designated path where the writer will be used
+ * @see opendal_operator
+ * @see opendal_result_operator_writer
+ * @see opendal_error
+ * @return Returns opendal_result_operator_writer, containing a writer and an 
opendal_error.
+ * If the operation succeeds, the `writer` field holds a valid writer and the 
`error` field
+ * is null. Otherwise, the `writer` will be null and the `error` will be set 
correspondingly.
+ *
+ * # Example
+ *
+ * Following is an example
+ * ```C
+ * //...prepare your opendal_operator, named op for example
+ *
+ * opendal_result_operator_writer result = opendal_operator_writer(op, 
"/testpath");
+ * assert(result.error == NULL);
+ * opendal_writer *writer = result.writer;
+ * // Use the writer to write data...
+ * ```
+ *
+ * # Safety
+ *
+ * It is **safe** under the cases below
+ * * The memory pointed to by `path` must contain a valid nul terminator at 
the end of
+ *   the string.
+ *
+ * # Panic
+ *
+ * * If the `path` points to NULL, this function panics, i.e. exits with 
information
+ */
+struct opendal_result_operator_writer opendal_operator_writer(const struct 
opendal_operator *op,
+                                                              const char 
*path);
+
 /**
  * \brief Blockingly delete the object in `path`.
  *
@@ -1419,6 +1513,18 @@ struct opendal_result_reader_read 
opendal_reader_read(const struct opendal_reade
  */
 void opendal_reader_free(struct opendal_reader *ptr);
 
+/**
+ * \brief Write data to the writer.
+ */
+struct opendal_result_writer_write opendal_writer_write(const struct 
opendal_writer *writer,
+                                                        uint8_t *buf,
+                                                        uintptr_t len);
+
+/**
+ * \brief Frees the heap memory used by the opendal_writer.
+ */
+void opendal_writer_free(struct opendal_writer *ptr);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif // __cplusplus
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs
index cfd46f92ed..ac125b0d42 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/lib.rs
@@ -51,9 +51,11 @@ pub use result::opendal_result_list;
 pub use result::opendal_result_lister_next;
 pub use result::opendal_result_operator_new;
 pub use result::opendal_result_operator_reader;
+pub use result::opendal_result_operator_writer;
 pub use result::opendal_result_read;
 pub use result::opendal_result_reader_read;
 pub use result::opendal_result_stat;
+pub use result::opendal_result_writer_write;
 
 mod types;
 pub use types::opendal_bytes;
@@ -64,3 +66,6 @@ pub use entry::opendal_entry;
 
 mod reader;
 pub use reader::opendal_reader;
+
+mod writer;
+pub use writer::opendal_writer;
diff --git a/bindings/c/src/operator.rs b/bindings/c/src/operator.rs
index 71a1e52e68..c1af1e5717 100644
--- a/bindings/c/src/operator.rs
+++ b/bindings/c/src/operator.rs
@@ -391,6 +391,69 @@ pub unsafe extern "C" fn opendal_operator_reader(
     }
 }
 
+/// \brief Blockingly create a writer for the specified path.
+///
+/// This function prepares a writer that can be used to write data to the 
specified path
+/// using the provided operator. If successful, it returns a valid writer; 
otherwise, it
+/// returns an error.
+///
+/// @param op The opendal_operator created previously
+/// @param path The designated path where the writer will be used
+/// @see opendal_operator
+/// @see opendal_result_operator_writer
+/// @see opendal_error
+/// @return Returns opendal_result_operator_writer, containing a writer and an 
opendal_error.
+/// If the operation succeeds, the `writer` field holds a valid writer and the 
`error` field
+/// is null. Otherwise, the `writer` will be null and the `error` will be set 
correspondingly.
+///
+/// # Example
+///
+/// Following is an example
+/// ```C
+/// //...prepare your opendal_operator, named op for example
+///
+/// opendal_result_operator_writer result = opendal_operator_writer(op, 
"/testpath");
+/// assert(result.error == NULL);
+/// opendal_writer *writer = result.writer;
+/// // Use the writer to write data...
+/// ```
+///
+/// # Safety
+///
+/// It is **safe** under the cases below
+/// * The memory pointed to by `path` must contain a valid nul terminator at 
the end of
+///   the string.
+///
+/// # Panic
+///
+/// * If the `path` points to NULL, this function panics, i.e. exits with 
information
+#[no_mangle]
+pub unsafe extern "C" fn opendal_operator_writer(
+    op: *const opendal_operator,
+    path: *const c_char,
+) -> opendal_result_operator_writer {
+    if path.is_null() {
+        panic!("The path given is pointing at NULL");
+    }
+    let op = (*op).as_ref();
+
+    let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
+    let writer = match op.writer(path) {
+        Ok(writer) => writer,
+        Err(err) => {
+            return opendal_result_operator_writer {
+                writer: std::ptr::null_mut(),
+                error: opendal_error::new(err),
+            }
+        }
+    };
+
+    opendal_result_operator_writer {
+        writer: 
Box::into_raw(Box::new(opendal_writer::new(writer.into_std_write()))),
+        error: std::ptr::null_mut(),
+    }
+}
+
 /// \brief Blockingly delete the object in `path`.
 ///
 /// Delete the object in `path` blockingly by `op_ptr`.
diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs
index 64b2789228..ba7e1c64a0 100644
--- a/bindings/c/src/result.rs
+++ b/bindings/c/src/result.rs
@@ -131,3 +131,25 @@ pub struct opendal_result_reader_read {
     /// The error, if ok, it is null
     pub error: *mut opendal_error,
 }
+
+/// \brief The result type returned by opendal_operator_writer().
+/// The result type for opendal_operator_writer(), the field `writer` contains 
the writer
+/// of the path, which is an iterator of the objects under the path. the field 
`code` represents
+#[repr(C)]
+pub struct opendal_result_operator_writer {
+    /// The pointer for opendal_writer
+    pub writer: *mut opendal_writer,
+    /// The error, if ok, it is null
+    pub error: *mut opendal_error,
+}
+
+/// \brief The result type returned by opendal_writer_write().
+/// The result type contains a size field, which is the size of the data 
written,
+/// which is zero on error. The error field is the error code and error 
message.
+#[repr(C)]
+pub struct opendal_result_writer_write {
+    /// The write size if succeed.
+    pub size: usize,
+    /// The error, if ok, it is null
+    pub error: *mut opendal_error,
+}
diff --git a/bindings/c/src/writer.rs b/bindings/c/src/writer.rs
new file mode 100644
index 0000000000..1c4fb33044
--- /dev/null
+++ b/bindings/c/src/writer.rs
@@ -0,0 +1,60 @@
+use std::io::Write;
+
+use ::opendal as core;
+
+use super::*;
+
+/// \brief The result type returned by opendal's writer operation.
+/// \note The opendal_writer actually owns a pointer to
+/// a opendal::BlockingWriter, which is inside the Rust core code.
+#[repr(C)]
+pub struct opendal_writer {
+    inner: *mut core::StdWriter,
+}
+
+impl opendal_writer {
+    pub(crate) fn new(writer: core::StdWriter) -> Self {
+        Self {
+            inner: Box::into_raw(Box::new(writer)),
+        }
+    }
+
+    /// \brief Write data to the writer.
+    #[no_mangle]
+    pub unsafe extern "C" fn opendal_writer_write(
+        writer: *const Self,
+        buf: *mut u8,
+        len: usize,
+    ) -> opendal_result_writer_write {
+        if buf.is_null() {
+            panic!("The buffer given is pointing at NULL");
+        }
+
+        let buf = unsafe { std::slice::from_raw_parts(buf, len) };
+
+        let inner = unsafe { &mut *(*writer).inner };
+        let n = inner.write(buf);
+        match n {
+            Ok(n) => opendal_result_writer_write {
+                size: n,
+                error: std::ptr::null_mut(),
+            },
+            Err(e) => opendal_result_writer_write {
+                size: 0,
+                error: opendal_error::new(
+                    core::Error::new(core::ErrorKind::Unexpected, "write 
failed from writer")
+                        .set_source(e),
+                ),
+            },
+        }
+    }
+
+    /// \brief Frees the heap memory used by the opendal_writer.
+    #[no_mangle]
+    pub unsafe extern "C" fn opendal_writer_free(ptr: *mut opendal_writer) {
+        if !ptr.is_null() {
+            let _ = unsafe { Box::from_raw((*ptr).inner) };
+            let _ = unsafe { Box::from_raw(ptr) };
+        }
+    }
+}
diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp
index 370c17fd5b..38f27df36b 100644
--- a/bindings/c/tests/bdd.cpp
+++ b/bindings/c/tests/bdd.cpp
@@ -90,6 +90,20 @@ TEST_F(OpendalBddTest, FeatureTest)
         EXPECT_EQ(this->content[i], (char)(r.data->data[i]));
     }
 
+    // The blocking file should be deleted
+    error = opendal_operator_delete(this->p, this->path.c_str());
+    EXPECT_EQ(error, nullptr);
+    e = opendal_operator_is_exist(this->p, this->path.c_str());
+    EXPECT_EQ(e.error, nullptr);
+    EXPECT_FALSE(e.is_exist);
+
+    opendal_result_operator_writer writer = opendal_operator_writer(this->p, 
this->path.c_str());
+    EXPECT_EQ(writer.error, nullptr);
+    opendal_result_writer_write w = opendal_writer_write(writer.writer, 
(unsigned char*)this->content.c_str(), this->content.length());
+    EXPECT_EQ(w.error, nullptr);
+    EXPECT_EQ(w.size, this->content.length());
+    opendal_writer_free(writer.writer);
+
     // The blocking file "test" must have content "Hello, World!" and read 
into buffer
     int length = this->content.length();
     unsigned char buffer[this->content.length()];
@@ -102,13 +116,6 @@ TEST_F(OpendalBddTest, FeatureTest)
     }
     opendal_reader_free(reader.reader);
 
-    // The blocking file should be deleted
-    error = opendal_operator_delete(this->p, this->path.c_str());
-    EXPECT_EQ(error, nullptr);
-    e = opendal_operator_is_exist(this->p, this->path.c_str());
-    EXPECT_EQ(e.error, nullptr);
-    EXPECT_FALSE(e.is_exist);
-
     // The deletion operation should be idempotent
     error = opendal_operator_delete(this->p, this->path.c_str());
     EXPECT_EQ(error, nullptr);
diff --git a/core/src/types/blocking_write/std_writer.rs 
b/core/src/types/blocking_write/std_writer.rs
index 91accbde53..56dfd8c1cf 100644
--- a/core/src/types/blocking_write/std_writer.rs
+++ b/core/src/types/blocking_write/std_writer.rs
@@ -112,9 +112,6 @@ impl Write for StdWriter {
 
 impl Drop for StdWriter {
     fn drop(&mut self) {
-        if let Some(mut w) = self.w.take() {
-            // Ignore error happens in close.
-            let _ = w.close();
-        }
+        let _ = self.close();
     }
 }

Reply via email to