diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index a709c2fcfec..f4ad4704f6e 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -112,24 +112,22 @@ typedef enum opendal_code { typedef struct BlockingOperator BlockingOperator; /* - The [`OperatorPtr`] owns a pointer to a [`od::BlockingOperator`]. + The [`opendal_operator_ptr`] owns a pointer to a [`od::BlockingOperator`]. It is also the key struct that OpenDAL's APIs access the real operator's memory. The use of OperatorPtr is zero cost, it only returns a reference of the underlying Operator. - The [`OperatorPtr`] also has a transparent layout, allowing you + The [`opendal_operator_ptr`] also has a transparent layout, allowing you to check its validity by native boolean operator. - e.g. you could check by (!ptr) on a opendal_operator_ptr type + e.g. you could check by (!ptr) on a [`opendal_operator_ptr`] */ typedef const struct BlockingOperator *opendal_operator_ptr; /* - The [`Bytes`] type is a C-compatible substitute for [`Bytes`] + The [`opendal_bytes`] type is a C-compatible substitute for [`Vec`] in Rust, it will not be deallocated automatically like what has been done in Rust. Instead, you have to call [`opendal_free_bytes`] to free the heap memory to avoid memory leak. - The field `data` should not be modified since it might causes - the reallocation of the Vector. */ typedef struct opendal_bytes { const uint8_t *data; @@ -147,6 +145,16 @@ typedef struct opendal_result_read { enum opendal_code code; } opendal_result_read; +/* + The result type for [`opendal_operator_is_exist()`], the field `is_exist` + contains whether the path exists, and the field `code` contains the + corresponding error code. + */ +typedef struct opendal_result_is_exist { + bool is_exist; + enum opendal_code code; +} opendal_result_is_exist; + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -178,7 +186,10 @@ void opendal_operator_free(opendal_operator_ptr op_ptr); It is [safe] under two cases below * The memory pointed to by `path` must contain a valid nul terminator at the end of the string. - * The `path` points to NULL, this function simply returns you false + + # Panic + + * If the `path` points to NULL, this function panics */ enum opendal_code opendal_operator_blocking_write(opendal_operator_ptr op_ptr, const char *path, @@ -194,15 +205,39 @@ enum opendal_code opendal_operator_blocking_write(opendal_operator_ptr op_ptr, It is [safe] under two cases below * The memory pointed to by `path` must contain a valid nul terminator at the end of the string. - * The `path` points to NULL, this function simply returns you a nullptr + + # Panic + + * If the `path` points to NULL, this function panics */ struct opendal_result_read opendal_operator_blocking_read(opendal_operator_ptr op_ptr, const char *path); +/* + Check whether the path exists. + + If the operation succeeds, no matter the path exists or not, + the error code should be opendal_code::OPENDAL_OK. Otherwise, + the field `is_exist` is filled with false, and the error code + is set correspondingly. + + # Safety + + It is [safe] under two 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_is_exist opendal_operator_is_exist(opendal_operator_ptr op_ptr, + const char *path); + /* Frees the heap memory used by the [`opendal_bytes`] */ -void opendal_bytes_free(const struct opendal_bytes *vec); +void opendal_bytes_free(const struct opendal_bytes *self); #ifdef __cplusplus } // extern "C" diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 33d4488dee4..943e4fa0f44 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -30,7 +30,7 @@ use crate::types::{opendal_bytes, opendal_operator_ptr}; use ::opendal as od; use error::opendal_code; -use result::opendal_result_read; +use result::{opendal_result_is_exist, opendal_result_read}; /// Returns a result type [`opendal_result_op`], with operator_ptr. If the construction succeeds /// the error is nullptr, otherwise it contains the error information. @@ -90,7 +90,10 @@ pub extern "C" fn opendal_operator_free(op_ptr: opendal_operator_ptr) { /// It is [safe] under two cases below /// * The memory pointed to by `path` must contain a valid nul terminator at the end of /// the string. -/// * The `path` points to NULL, this function simply returns you false +/// +/// # Panic +/// +/// * If the `path` points to NULL, this function panics #[no_mangle] pub unsafe extern "C" fn opendal_operator_blocking_write( op_ptr: opendal_operator_ptr, @@ -98,7 +101,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write( bytes: opendal_bytes, ) -> opendal_code { if path.is_null() { - return opendal_code::OPENDAL_ERROR; + panic!("The path given is pointing at NULL"); } let op = op_ptr.get_ref(); @@ -118,17 +121,17 @@ pub unsafe extern "C" fn opendal_operator_blocking_write( /// It is [safe] under two cases below /// * The memory pointed to by `path` must contain a valid nul terminator at the end of /// the string. -/// * The `path` points to NULL, this function simply returns you a nullptr +/// +/// # Panic +/// +/// * If the `path` points to NULL, this function panics #[no_mangle] pub unsafe extern "C" fn opendal_operator_blocking_read( op_ptr: opendal_operator_ptr, path: *const c_char, ) -> opendal_result_read { if path.is_null() { - return opendal_result_read { - data: std::ptr::null_mut(), - code: opendal_code::OPENDAL_ERROR, - }; + panic!("The path given is pointing at NULL"); } let op = op_ptr.get_ref(); @@ -148,3 +151,42 @@ pub unsafe extern "C" fn opendal_operator_blocking_read( }, } } + +/// Check whether the path exists. +/// +/// If the operation succeeds, no matter the path exists or not, +/// the error code should be opendal_code::OPENDAL_OK. Otherwise, +/// the field `is_exist` is filled with false, and the error code +/// is set correspondingly. +/// +/// # Safety +/// +/// It is [safe] under two 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_is_exist( + op_ptr: opendal_operator_ptr, + path: *const c_char, +) -> opendal_result_is_exist { + 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.is_exist(path) { + Ok(e) => opendal_result_is_exist { + is_exist: e, + code: opendal_code::OPENDAL_OK, + }, + Err(err) => opendal_result_is_exist { + is_exist: false, + code: opendal_code::from_opendal_error(err), + }, + } +} diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs index fe69dd35de2..69b2c5ba3b4 100644 --- a/bindings/c/src/result.rs +++ b/bindings/c/src/result.rs @@ -31,3 +31,12 @@ pub struct opendal_result_read { pub data: *mut opendal_bytes, pub code: opendal_code, } + +/// The result type for [`opendal_operator_is_exist()`], the field `is_exist` +/// contains whether the path exists, and the field `code` contains the +/// corresponding error code. +#[repr(C)] +pub struct opendal_result_is_exist { + pub is_exist: bool, + pub code: opendal_code, +} diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index f3b40d8223f..b8752fa0893 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -17,23 +17,23 @@ use ::opendal as od; -/// The [`OperatorPtr`] owns a pointer to a [`od::BlockingOperator`]. +/// The [`opendal_operator_ptr`] owns a pointer to a [`od::BlockingOperator`]. /// It is also the key struct that OpenDAL's APIs access the real /// operator's memory. The use of OperatorPtr is zero cost, it /// only returns a reference of the underlying Operator. /// -/// The [`OperatorPtr`] also has a transparent layout, allowing you +/// The [`opendal_operator_ptr`] also has a transparent layout, allowing you /// to check its validity by native boolean operator. -/// e.g. you could check by (!ptr) on a opendal_operator_ptr type +/// e.g. you could check by (!ptr) on a [`opendal_operator_ptr`] #[repr(transparent)] pub struct opendal_operator_ptr { ptr: *const od::BlockingOperator, } impl opendal_operator_ptr { - /// Creates an OperatorPtr will nullptr, indicating this [`OperatorPtr`] + /// Creates an OperatorPtr will nullptr, indicating this [`opendal_operator_ptr`] /// is invalid. The `transparent` layout also guarantees that if the - /// underlying field `ptr` is a nullptr, the [`OperatorPtr`] has the + /// underlying field `ptr` is a nullptr, the [`opendal_operator_ptr`] has the /// same layout as the nullptr. pub(crate) fn null() -> Self { Self { @@ -46,7 +46,7 @@ impl opendal_operator_ptr { self.ptr.is_null() } - /// Returns a reference to the underlying [`BlockingOperator`] + /// Returns a reference to the underlying [`od::BlockingOperator`] pub(crate) fn get_ref(&self) -> &od::BlockingOperator { unsafe { &*(self.ptr) } } @@ -74,12 +74,10 @@ impl From<&mut od::BlockingOperator> for opendal_operator_ptr { } } -/// The [`Bytes`] type is a C-compatible substitute for [`Bytes`] +/// The [`opendal_bytes`] type is a C-compatible substitute for [`Vec`] /// in Rust, it will not be deallocated automatically like what /// has been done in Rust. Instead, you have to call [`opendal_free_bytes`] /// to free the heap memory to avoid memory leak. -/// The field `data` should not be modified since it might causes -/// the reallocation of the Vector. #[repr(C)] pub struct opendal_bytes { pub data: *const u8, @@ -87,13 +85,23 @@ pub struct opendal_bytes { } impl opendal_bytes { - /// Construct a [`Vector`] from the Rust [`Vec`] of bytes + /// Construct a [`opendal_bytes`] from the Rust [`Vec`] of bytes pub(crate) fn from_vec(vec: Vec) -> Self { let data = vec.as_ptr() as *const u8; let len = vec.len(); std::mem::forget(vec); // To avoid deallocation of the vec. Self { data, len } } + + /// Frees the heap memory used by the [`opendal_bytes`] + #[no_mangle] + pub extern "C" fn opendal_bytes_free(&self) { + unsafe { + // this deallocates the vector by reconstructing the vector and letting + // it be dropped when its out of scope + Vec::from_raw_parts(self.data as *mut u8, self.len, self.len); + } + } } #[allow(clippy::from_over_into)] @@ -103,13 +111,3 @@ impl Into for opendal_bytes { bytes::Bytes::from_static(slice) } } - -/// Frees the heap memory used by the [`opendal_bytes`] -#[no_mangle] -pub extern "C" fn opendal_bytes_free(vec: *const opendal_bytes) { - unsafe { - // this deallocates the vector by reconstructing the vector and letting - // it be dropped when its out of scope - Vec::from_raw_parts((*vec).data as *mut u8, (*vec).len, (*vec).len); - } -}