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

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 20d82324 feat(bindings/C): Initially support stat in C binding (#2249)
20d82324 is described below

commit 20d82324b4eabe307316a3567a09f731c32a2ea2
Author: xyJi <[email protected]>
AuthorDate: Wed May 10 16:28:54 2023 +0800

    feat(bindings/C): Initially support stat in C binding (#2249)
    
    * basic support of stat
    
    Signed-off-by: Ji-Xinyou <[email protected]>
    
    * content length
    
    Signed-off-by: Ji-Xinyou <[email protected]>
    
    * more docs
    
    Signed-off-by: Ji-Xinyou <[email protected]>
    
    * format basicio.c
    
    Signed-off-by: Ji-Xinyou <[email protected]>
    
    * typo
    
    Signed-off-by: Ji-Xinyou <[email protected]>
    
    * no abbreviation for meta
    
    Signed-off-by: Ji-Xinyou <[email protected]>
    
    ---------
    
    Signed-off-by: Ji-Xinyou <[email protected]>
---
 bindings/c/include/opendal.h | 88 ++++++++++++++++++++++++++++++++++++++++----
 bindings/c/src/lib.rs        | 59 +++++++++++++++++++++--------
 bindings/c/src/result.rs     | 11 +++++-
 bindings/c/src/types.rs      | 83 +++++++++++++++++++++++++++++++++++++----
 bindings/c/tests/basicio.c   | 26 +++++++++++--
 5 files changed, 231 insertions(+), 36 deletions(-)

diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index f4ad4704..2d4e82fd 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -111,6 +111,17 @@ typedef enum opendal_code {
  */
 typedef struct BlockingOperator BlockingOperator;
 
+/*
+ Metadata carries all metadata associated with an path.
+
+ # Notes
+
+ mode and content_length are required metadata that all services
+ should provide during `stat` operation. But in `list` operation,
+ a.k.a., `Entry`'s content length could be `None`.
+ */
+typedef struct Metadata Metadata;
+
 /*
  The [`opendal_operator_ptr`] owns a pointer to a [`od::BlockingOperator`].
  It is also the key struct that OpenDAL's APIs access the real
@@ -155,6 +166,27 @@ typedef struct opendal_result_is_exist {
   enum opendal_code code;
 } opendal_result_is_exist;
 
+/*
+ Metadata carries all metadata associated with an path.
+
+ # Notes
+
+ mode and content_length are required metadata that all services
+ should provide during `stat` operation. But in `list` operation,
+ a.k.a., `Entry`'s content length could be NULL.
+ */
+typedef const struct Metadata *opendal_metadata;
+
+/*
+ The result type for [`opendal_operator_stat()`], the meta contains the 
metadata
+ of the path, the code represents whether the stat operation is successful. 
Note
+ that the operation could be successful even if the path does not exist.
+ */
+typedef struct opendal_result_stat {
+  opendal_metadata meta;
+  enum opendal_code code;
+} opendal_result_stat;
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -172,18 +204,13 @@ extern "C" {
  */
 opendal_operator_ptr opendal_operator_new(const char *scheme);
 
-/*
- Free the allocated operator pointed by [`opendal_operator_ptr`]
- */
-void opendal_operator_free(opendal_operator_ptr op_ptr);
-
 /*
  Write the data into the path blockingly by operator, returns the error code 
OPENDAL_OK
  if succeeds, others otherwise
 
  # Safety
 
- It is [safe] under two cases below
+ 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.
 
@@ -202,7 +229,7 @@ enum opendal_code 
opendal_operator_blocking_write(opendal_operator_ptr op_ptr,
 
  # Safety
 
- It is [safe] under two cases below
+ 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.
 
@@ -223,7 +250,7 @@ struct opendal_result_read 
opendal_operator_blocking_read(opendal_operator_ptr o
 
  # Safety
 
- It is [safe] under two cases below
+ 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.
 
@@ -234,11 +261,56 @@ struct opendal_result_read 
opendal_operator_blocking_read(opendal_operator_ptr o
 struct opendal_result_is_exist opendal_operator_is_exist(opendal_operator_ptr 
op_ptr,
                                                          const char *path);
 
+/*
+ Stat the path, return its metadata.
+
+ If the operation succeeds, no matter the path exists or not,
+ the error code should be opendal_code::OPENDAL_OK. Otherwise,
+ the field `meata` is filled with a NULL pointer, and the error code
+ is set correspondingly.
+
+ # 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
+ */
+struct opendal_result_stat opendal_operator_stat(opendal_operator_ptr op_ptr, 
const char *path);
+
+/*
+ Free the allocated operator pointed by [`opendal_operator_ptr`]
+ */
+void opendal_operator_free(const opendal_operator_ptr *self);
+
 /*
  Frees the heap memory used by the [`opendal_bytes`]
  */
 void opendal_bytes_free(const struct opendal_bytes *self);
 
+/*
+ Free the allocated metadata
+ */
+void opendal_metadata_free(const opendal_metadata *self);
+
+/*
+ Return the content_length of the metadata
+ */
+uint64_t opendal_metadata_content_length(const opendal_metadata *self);
+
+/*
+ Return whether the path represents a file
+ */
+bool opendal_metadata_is_file(const opendal_metadata *self);
+
+/*
+ Return whether the path represents a directory
+ */
+bool opendal_metadata_is_dir(const opendal_metadata *self);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif // __cplusplus
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs
index c248502e..649154ea 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/lib.rs
@@ -28,8 +28,8 @@ use std::str::FromStr;
 
 use ::opendal as od;
 use error::opendal_code;
-use result::opendal_result_is_exist;
-use result::opendal_result_read;
+use result::{opendal_result_is_exist, opendal_result_read, 
opendal_result_stat};
+use types::opendal_metadata;
 
 use crate::types::opendal_bytes;
 use crate::types::opendal_operator_ptr;
@@ -74,22 +74,12 @@ pub unsafe extern "C" fn opendal_operator_new(scheme: 
*const c_char) -> opendal_
     opendal_operator_ptr::from(op)
 }
 
-/// Free the allocated operator pointed by [`opendal_operator_ptr`]
-#[no_mangle]
-pub extern "C" fn opendal_operator_free(op_ptr: opendal_operator_ptr) {
-    if op_ptr.is_null() {
-        return;
-    }
-    let _ = unsafe { Box::from_raw(op_ptr.get_ref_mut()) };
-    // dropped
-}
-
 /// Write the data into the path blockingly by operator, returns the error 
code OPENDAL_OK
 /// if succeeds, others otherwise
 ///
 /// # Safety
 ///
-/// It is [safe] under two cases below
+/// 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.
 ///
@@ -120,7 +110,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
 ///
 /// # Safety
 ///
-/// It is [safe] under two cases below
+/// 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.
 ///
@@ -163,7 +153,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
 ///
 /// # Safety
 ///
-/// It is [safe] under two cases below
+/// 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.
 ///
@@ -192,3 +182,42 @@ pub unsafe extern "C" fn opendal_operator_is_exist(
         },
     }
 }
+
+/// Stat the path, return its metadata.
+///
+/// If the operation succeeds, no matter the path exists or not,
+/// the error code should be opendal_code::OPENDAL_OK. Otherwise,
+/// the field `meata` is filled with a NULL pointer, and the error code
+/// is set correspondingly.
+///
+/// # 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
+#[no_mangle]
+pub unsafe extern "C" fn opendal_operator_stat(
+    op_ptr: opendal_operator_ptr,
+    path: *const c_char,
+) -> opendal_result_stat {
+    if path.is_null() {
+        panic!("The path given is pointing at NULL");
+    }
+
+    let op = op_ptr.get_ref();
+    let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
+    match op.stat(path) {
+        Ok(m) => opendal_result_stat {
+            meta: opendal_metadata::from_metadata(m),
+            code: opendal_code::OPENDAL_OK,
+        },
+        Err(err) => opendal_result_stat {
+            meta: opendal_metadata::null(),
+            code: opendal_code::from_opendal_error(err),
+        },
+    }
+}
diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs
index 6f869973..36591c30 100644
--- a/bindings/c/src/result.rs
+++ b/bindings/c/src/result.rs
@@ -21,7 +21,7 @@
 //! we are defining all Result types here
 
 use crate::error::opendal_code;
-use crate::types::opendal_bytes;
+use crate::types::{opendal_bytes, opendal_metadata};
 
 /// The Rust-like Result type of opendal C binding, it contains
 /// the data that the read operation returns and a error code
@@ -41,3 +41,12 @@ pub struct opendal_result_is_exist {
     pub is_exist: bool,
     pub code: opendal_code,
 }
+
+/// The result type for [`opendal_operator_stat()`], the meta contains the 
metadata
+/// of the path, the code represents whether the stat operation is successful. 
Note
+/// that the operation could be successful even if the path does not exist.
+#[repr(C)]
+pub struct opendal_result_stat {
+    pub meta: opendal_metadata,
+    pub code: opendal_code,
+}
diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs
index b8752fa0..5bbebb02 100644
--- a/bindings/c/src/types.rs
+++ b/bindings/c/src/types.rs
@@ -30,6 +30,17 @@ pub struct opendal_operator_ptr {
     ptr: *const od::BlockingOperator,
 }
 
+impl opendal_operator_ptr {
+    /// Free the allocated operator pointed by [`opendal_operator_ptr`]
+    #[no_mangle]
+    pub extern "C" fn opendal_operator_free(&self) {
+        if self.is_null() {
+            return;
+        }
+        let _ = unsafe { Box::from_raw(self.ptr as *mut od::BlockingOperator) 
};
+    }
+}
+
 impl opendal_operator_ptr {
     /// Creates an OperatorPtr will nullptr, indicating this 
[`opendal_operator_ptr`]
     /// is invalid. The `transparent` layout also guarantees that if the
@@ -50,14 +61,6 @@ impl opendal_operator_ptr {
     pub(crate) fn get_ref(&self) -> &od::BlockingOperator {
         unsafe { &*(self.ptr) }
     }
-
-    /// Returns a mutable reference to the underlying [`od::BlockingOperator`].
-    /// Note that this should be only used when the operator is being freed
-    #[allow(clippy::mut_from_ref)]
-    pub(crate) fn get_ref_mut(&self) -> &mut od::BlockingOperator {
-        let ptr_mut = self.ptr as *mut od::BlockingOperator;
-        unsafe { &mut (*ptr_mut) }
-    }
 }
 
 #[allow(clippy::from_over_into)]
@@ -111,3 +114,67 @@ impl Into<bytes::Bytes> for opendal_bytes {
         bytes::Bytes::from_static(slice)
     }
 }
+
+/// Metadata carries all metadata associated with an path.
+///
+/// # Notes
+///
+/// mode and content_length are required metadata that all services
+/// should provide during `stat` operation. But in `list` operation,
+/// a.k.a., `Entry`'s content length could be NULL.
+#[repr(transparent)]
+pub struct opendal_metadata {
+    pub inner: *const od::Metadata,
+}
+
+impl opendal_metadata {
+    /// Free the allocated metadata
+    #[no_mangle]
+    pub extern "C" fn opendal_metadata_free(&self) {
+        if self.inner.is_null() {
+            return;
+        }
+        let _ = unsafe { Box::from_raw(self.inner as *mut od::Metadata) };
+    }
+
+    /// Return the content_length of the metadata
+    #[no_mangle]
+    pub extern "C" fn opendal_metadata_content_length(&self) -> u64 {
+        // Safety: the inner should never be null once constructed
+        // The use-after-free is undefined behavior
+        unsafe { (*self.inner).content_length() }
+    }
+
+    /// Return whether the path represents a file
+    #[no_mangle]
+    pub extern "C" fn opendal_metadata_is_file(&self) -> bool {
+        // Safety: the inner should never be null once constructed
+        // The use-after-free is undefined behavior
+        unsafe { (*self.inner).is_file() }
+    }
+
+    /// Return whether the path represents a directory
+    #[no_mangle]
+    pub extern "C" fn opendal_metadata_is_dir(&self) -> bool {
+        // Safety: the inner should never be null once constructed
+        // The use-after-free is undefined behavior
+        unsafe { (*self.inner).is_dir() }
+    }
+}
+
+impl opendal_metadata {
+    /// Return a null metadata
+    pub(crate) fn null() -> Self {
+        Self {
+            inner: std::ptr::null(),
+        }
+    }
+
+    /// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible
+    /// [`opendal_metadata`]
+    pub(crate) fn from_metadata(m: od::Metadata) -> Self {
+        Self {
+            inner: Box::leak(Box::new(m)),
+        }
+    }
+}
diff --git a/bindings/c/tests/basicio.c b/bindings/c/tests/basicio.c
index 765669f2..36524594 100644
--- a/bindings/c/tests/basicio.c
+++ b/bindings/c/tests/basicio.c
@@ -27,13 +27,12 @@
 // * A valid ptr is given
 // * The blocking write operation is successful
 // * The blocking read operation is successful and works as expected
-void test_operator_rw(opendal_operator_ptr ptr)
+void test_operator_rw(opendal_operator_ptr ptr, char* path)
 {
     // have to be valid ptr
     assert(ptr);
 
     // write some contents by the operator, must be successful
-    char path[] = "test";
     char content[] = "Hello World";
     const opendal_bytes data = {
         .len = sizeof(content) - 1,
@@ -55,17 +54,36 @@ void test_operator_rw(opendal_operator_ptr ptr)
     opendal_bytes_free(r.data);
 }
 
+void test_operator_stat(opendal_operator_ptr ptr, char* path)
+{
+    assert(ptr);
+    opendal_result_stat r = opendal_operator_stat(ptr, path);
+    assert(r.code == OPENDAL_OK);
+
+    opendal_metadata meta = r.meta;
+    assert(meta);
+
+    assert(opendal_metadata_is_file(&meta));
+
+    uint64_t content_length = opendal_metadata_content_length(&meta);
+    assert(content_length == 11);
+
+    opendal_metadata_free(&meta);
+}
+
 int main(int argc, char* argv[])
 {
     // construct the memory operator
     char scheme1[] = "memory";
+    char path[] = "test";
     opendal_operator_ptr p1 = opendal_operator_new(scheme1);
     assert(p1);
 
-    test_operator_rw(p1);
+    test_operator_rw(p1, path);
+    test_operator_stat(p1, path);
 
     // free the operator
-    opendal_operator_free(p1);
+    opendal_operator_free(&p1);
 
     return 0;
 }

Reply via email to