diff --git a/bindings/c/Makefile b/bindings/c/Makefile index e10f47bdf4b0..245d4facbb11 100644 --- a/bindings/c/Makefile +++ b/bindings/c/Makefile @@ -32,6 +32,7 @@ all: build test examples .PHONY: format format: + cargo fmt find . -name '*.cpp' -exec clang-format -i --style=WebKit --verbose {} \; find . -name '*.c' -exec clang-format -i --style=WebKit --verbose {} \; diff --git a/bindings/c/README.md b/bindings/c/README.md index 908f8d5cdecf..1abb0131c09a 100644 --- a/bindings/c/README.md +++ b/bindings/c/README.md @@ -16,8 +16,9 @@ A simple read and write example int main() { /* Initialize a operator for "memory" backend, with no options */ - opendal_operator_ptr op = opendal_operator_new("memory", 0); - assert(op.ptr != NULL); + opendal_result_operator_new result = opendal_operator_new("memory", 0); + assert(result.operator_ptr != NULL); + assert(result.error == NULL); /* Prepare some data to be written */ opendal_bytes data = { @@ -26,11 +27,11 @@ int main() }; /* Write this into path "/testpath" */ - opendal_error *error = opendal_operator_blocking_write(op, "/testpath", data); + opendal_error *error = opendal_operator_write(op, "/testpath", data); assert(error == NULL); /* We can read it out, make sure the data is the same */ - opendal_result_read r = opendal_operator_blocking_read(op, "/testpath"); + opendal_result_read r = opendal_operator_read(op, "/testpath"); opendal_bytes* read_bytes = r.data; assert(r.error == NULL); assert(read_bytes->len == 24); diff --git a/bindings/c/examples/basic.c b/bindings/c/examples/basic.c index 88071847c345..5e4e7925ba77 100644 --- a/bindings/c/examples/basic.c +++ b/bindings/c/examples/basic.c @@ -25,10 +25,10 @@ int main() { /* Initialize a operator for "memory" backend, with no options */ opendal_result_operator_new result = opendal_operator_new("memory", 0); - assert(result.operator_ptr != NULL); + assert(result.op != NULL); assert(result.error == NULL); - opendal_operator_ptr* op = result.operator_ptr; + opendal_operator* op = result.op; /* Prepare some data to be written */ opendal_bytes data = { @@ -37,11 +37,11 @@ int main() }; /* Write this into path "/testpath" */ - opendal_error* error = opendal_operator_blocking_write(op, "/testpath", data); + opendal_error* error = opendal_operator_write(op, "/testpath", data); assert(error == NULL); /* We can read it out, make sure the data is the same */ - opendal_result_read r = opendal_operator_blocking_read(op, "/testpath"); + opendal_result_read r = opendal_operator_read(op, "/testpath"); opendal_bytes* read_bytes = r.data; assert(r.error == NULL); assert(read_bytes->len == 24); diff --git a/bindings/c/examples/error_handle.c b/bindings/c/examples/error_handle.c index 3bb3308e7978..193a788afa97 100644 --- a/bindings/c/examples/error_handle.c +++ b/bindings/c/examples/error_handle.c @@ -26,13 +26,13 @@ int main() { /* Initialize a operator for "memory" backend, with no options */ opendal_result_operator_new result = opendal_operator_new("memory", 0); - assert(result.operator_ptr != NULL); + assert(result.op != NULL); assert(result.error == NULL); - opendal_operator_ptr* op = result.operator_ptr; + opendal_operator* op = result.op; /* The read is supposed to fail */ - opendal_result_read r = opendal_operator_blocking_read(op, "/testpath"); + opendal_result_read r = opendal_operator_read(op, "/testpath"); assert(r.error != NULL); assert(r.error->code == OPENDAL_NOT_FOUND); diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index 044a451b5fa9..cc43391513fc 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -207,27 +207,6 @@ typedef struct HashMap_String__String HashMap_String__String; */ typedef struct Metadata Metadata; -/** - * \brief Used to access almost all OpenDAL APIs. It represents a - * operator that provides the unified interfaces provided by OpenDAL. - * - * @see opendal_operator_new This function construct the operator - * @see opendal_operator_free This function frees the heap memory of the operator - * - * \note The opendal_operator_ptr actually owns a pointer to - * a opendal::BlockingOperator, which is inside the Rust core code. - * - * \remark You may use the field `ptr` to check whether this is a NULL - * operator. - */ -typedef struct opendal_operator_ptr { - /** - * The pointer to the opendal::BlockingOperator in the Rust code. - * Only touch this on judging whether it is NULL. - */ - const struct BlockingOperator *ptr; -} opendal_operator_ptr; - /** * \brief opendal_bytes carries raw-bytes with its length * @@ -272,25 +251,114 @@ typedef struct opendal_error { struct opendal_bytes message; } opendal_error; +/** + * \brief opendal_list_entry is the entry under a path, which is listed from the opendal_lister + * + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() + * @see opendal_list_entry_path() + * @see opendal_list_entry_name() + */ +typedef struct opendal_entry { + struct Entry *inner; +} opendal_entry; + +/** + * \brief The result type returned by opendal_lister_next(). + * The list entry is the list result of the list operation, the error field is the error code and error message. + * If the operation succeeds, the error should be NULL. + * + * \note Please notice if the lister reaches the end, both the list_entry and error will be NULL. + */ +typedef struct opendal_result_lister_next { + /** + * The next object name + */ + struct opendal_entry *entry; + /** + * The error, if ok, it is null + */ + struct opendal_error *error; +} opendal_result_lister_next; + +/** + * \brief BlockingLister is designed to list entries at given path in a blocking + * manner. + * + * Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) + * + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() + */ +typedef struct opendal_lister { + struct BlockingLister *inner; +} opendal_lister; + +/** + * \brief Carries all metadata associated with a path. + * + * The metadata of the "thing" under a path. Please **only** use the opendal_metadata + * with our provided API, e.g. opendal_metadata_content_length(). + * + * \note The metadata is also heap-allocated, please call opendal_metadata_free() on this + * to free the heap memory. + * + * @see opendal_metadata_free + */ +typedef struct opendal_metadata { + /** + * The pointer to the opendal::Metadata in the Rust code. + * Only touch this on judging whether it is NULL. + */ + struct Metadata *inner; +} opendal_metadata; + +/** + * \brief Used to access almost all OpenDAL APIs. It represents a + * operator that provides the unified interfaces provided by OpenDAL. + * + * @see opendal_operator_new This function construct the operator + * @see opendal_operator_free This function frees the heap memory of the operator + * + * \note The opendal_operator actually owns a pointer to + * a opendal::BlockingOperator, which is inside the Rust core code. + * + * \remark You may use the field `ptr` to check whether this is a NULL + * operator. + */ +typedef struct opendal_operator { + /** + * The pointer to the opendal::BlockingOperator in the Rust code. + * Only touch this on judging whether it is NULL. + */ + const struct BlockingOperator *ptr; +} opendal_operator; + /** * \brief The result type returned by opendal_operator_new() operation. * - * If the init logic is successful, the `operator_ptr` field will be set to a valid + * If the init logic is successful, the `op` field will be set to a valid * pointer, and the `error` field will be set to null. If the init logic fails, the - * `operator_ptr` field will be set to null, and the `error` field will be set to a + * `op` field will be set to null, and the `error` field will be set to a * valid pointer with error code and error message. * * @see opendal_operator_new() - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_error */ typedef struct opendal_result_operator_new { - struct opendal_operator_ptr *operator_ptr; + /** + * The pointer for operator. + */ + struct opendal_operator *op; + /** + * The error pointer for error. + */ struct opendal_error *error; } opendal_result_operator_new; /** - * \brief The configuration for the initialization of opendal_operator_ptr. + * \brief The configuration for the initialization of opendal_operator. * * \note This is also a heap-allocated struct, please free it after you use it * @@ -326,6 +394,12 @@ typedef struct opendal_result_read { struct opendal_error *error; } opendal_result_read; +/** + * \brief The result type returned by opendal's reader operation. + * + * \note The opendal_reader actually owns a pointer to + * a opendal::BlockingReader, which is inside the Rust core code. + */ typedef struct opendal_reader { struct BlockingReader *inner; } opendal_reader; @@ -336,10 +410,16 @@ typedef struct opendal_reader { * of the path, which is an iterator of the objects under the path. the field `code` represents * whether the stat operation is successful. */ -typedef struct opendal_result_reader { +typedef struct opendal_result_operator_reader { + /** + * The pointer for opendal_reader + */ struct opendal_reader *reader; + /** + * The error, if ok, it is null + */ struct opendal_error *error; -} opendal_result_reader; +} opendal_result_operator_reader; /** * \brief The result type returned by opendal_operator_is_exist(). @@ -362,25 +442,6 @@ typedef struct opendal_result_is_exist { struct opendal_error *error; } opendal_result_is_exist; -/** - * \brief Carries all metadata associated with a path. - * - * The metadata of the "thing" under a path. Please **only** use the opendal_metadata - * with our provided API, e.g. opendal_metadata_content_length(). - * - * \note The metadata is also heap-allocated, please call opendal_metadata_free() on this - * to free the heap memory. - * - * @see opendal_metadata_free - */ -typedef struct opendal_metadata { - /** - * The pointer to the opendal::Metadata in the Rust code. - * Only touch this on judging whether it is NULL. - */ - struct Metadata *inner; -} opendal_metadata; - /** * \brief The result type returned by opendal_operator_stat(). * @@ -400,22 +461,9 @@ typedef struct opendal_result_stat { } opendal_result_stat; /** - * \brief BlockingLister is designed to list entries at given path in a blocking - * manner. - * - * Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) - * - * For examples, please see the comment section of opendal_operator_blocking_list() - * @see opendal_operator_blocking_list() - */ -typedef struct opendal_blocking_lister { - struct BlockingLister *inner; -} opendal_blocking_lister; - -/** - * \brief The result type returned by opendal_operator_blocking_list(). + * \brief The result type returned by opendal_operator_list(). * - * The result type for opendal_operator_blocking_list(), the field `lister` contains the lister + * The result type for opendal_operator_list(), the field `lister` contains the lister * of the path, which is an iterator of the objects under the path. the field `error` represents * whether the stat operation is successful. If successful, the `error` field is null. */ @@ -423,7 +471,7 @@ typedef struct opendal_result_list { /** * The lister, used for further listing operations */ - struct opendal_blocking_lister *lister; + struct opendal_lister *lister; /** * The error, if ok, it is null */ @@ -431,19 +479,18 @@ typedef struct opendal_result_list { } opendal_result_list; /** - * \brief opendal_list_entry is the entry under a path, which is listed from the opendal_blocking_lister - * - * For examples, please see the comment section of opendal_operator_blocking_list() - * @see opendal_operator_blocking_list() - * @see opendal_list_entry_path() - * @see opendal_list_entry_name() + * \brief The is the result type returned by opendal_reader_read(). + * The result type contains a size field, which is the size of the data read, + * which is zero on error. The error field is the error code and error message. */ -typedef struct opendal_list_entry { - struct Entry *inner; -} opendal_list_entry; - typedef struct opendal_result_reader_read { + /** + * The read size if succeed. + */ uintptr_t size; + /** + * The error, if ok, it is null + */ struct opendal_error *error; } opendal_result_reader_read; @@ -451,6 +498,101 @@ typedef struct opendal_result_reader_read { extern "C" { #endif // __cplusplus +/** + * \brief Frees the opendal_error, ok to call on NULL + */ +void opendal_error_free(struct opendal_error *ptr); + +/** + * \brief Return the next object to be listed + * + * Lister is an iterator of the objects under its path, this method is the same as + * calling next() on the iterator + * + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() + */ +struct opendal_result_lister_next opendal_lister_next(const struct opendal_lister *self); + +/** + * \brief Free the heap-allocated metadata used by opendal_lister + */ +void opendal_lister_free(const struct opendal_lister *p); + +/** + * \brief Free the heap-allocated metadata used by opendal_metadata + */ +void opendal_metadata_free(struct opendal_metadata *ptr); + +/** + * \brief Return the content_length of the metadata + * + * # Example + * ```C + * // ... previously you wrote "Hello, World!" to path "/testpath" + * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + * assert(s.error == NULL); + * + * opendal_metadata *meta = s.meta; + * assert(opendal_metadata_content_length(meta) == 13); + * ``` + */ +uint64_t opendal_metadata_content_length(const struct opendal_metadata *self); + +/** + * \brief Return whether the path represents a file + * + * # Example + * ```C + * // ... previously you wrote "Hello, World!" to path "/testpath" + * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + * assert(s.error == NULL); + * + * opendal_metadata *meta = s.meta; + * assert(opendal_metadata_is_file(meta)); + * ``` + */ +bool opendal_metadata_is_file(const struct opendal_metadata *self); + +/** + * \brief Return whether the path represents a directory + * + * # Example + * ```C + * // ... previously you wrote "Hello, World!" to path "/testpath" + * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + * assert(s.error == NULL); + * + * opendal_metadata *meta = s.meta; + * + * // this is not a directory + * assert(!opendal_metadata_is_dir(meta)); + * ``` + * + * \todo This is not a very clear example. A clearer example will be added + * after we support opendal_operator_mkdir() + */ +bool opendal_metadata_is_dir(const struct opendal_metadata *self); + +/** + * \brief Free the heap-allocated operator pointed by opendal_operator. + * + * Please only use this for a pointer pointing at a valid opendal_operator. + * Calling this function on NULL does nothing, but calling this function on pointers + * of other type will lead to segfault. + * + * # Example + * + * ```C + * opendal_operator *ptr = opendal_operator_new("fs", NULL); + * // ... use this ptr, maybe some reads and writes + * + * // free this operator + * opendal_operator_free(ptr); + * ``` + */ +void opendal_operator_free(const struct opendal_operator *op); + /** * \brief Construct an operator based on `scheme` and `options` * @@ -464,8 +606,8 @@ extern "C" { * option is set * @see opendal_operator_options * @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction - * succeeds. On success the operator_ptr field is a valid pointer to a newly allocated opendal_operator_ptr, - * and the error field is NULL. Otherwise, the operator_ptr field is a NULL pointer and the error field. + * succeeds. On success the operator field is a valid pointer to a newly allocated opendal_operator, + * and the error field is NULL. Otherwise, the operator field is a NULL pointer and the error field. * * # Example * @@ -478,7 +620,7 @@ extern "C" { * * // Construct the operator based on the options and scheme * opendal_result_operator_new result = opendal_operator_new("memory", options); - * opendal_operator_ptr* op = result.operator_ptr; + * opendal_operator* op = result.op; * * // you could free the options right away since the options is not used afterwards * opendal_operator_options_free(options); @@ -502,10 +644,10 @@ struct opendal_result_operator_new opendal_operator_new(const char *scheme, * \note It is important to notice that the `bytes` that is passes in will be consumed by this * function. Therefore, you should not use the `bytes` after this function returns. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The designated path you want to write your bytes in * @param bytes The opendal_byte typed bytes to be written - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_bytes * @see opendal_error * @return NULL if succeeds, otherwise it contains the error code and error message. @@ -514,14 +656,14 @@ struct opendal_result_operator_new opendal_operator_new(const char *scheme, * * Following is an example * ```C - * //...prepare your opendal_operator_ptr, named ptr for example + * //...prepare your opendal_operator, named ptr for example * * // prepare your data * char* data = "Hello, World!"; * opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; * * // now you can write! - * opendal_error *err = opendal_operator_blocking_write(ptr, "/testpath", bytes); + * opendal_error *err = opendal_operator_write(ptr, "/testpath", bytes); * * // Assert that this succeeds * assert(err == NULL); @@ -539,18 +681,18 @@ struct opendal_result_operator_new opendal_operator_new(const char *scheme, * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_error *opendal_operator_blocking_write(const struct opendal_operator_ptr *ptr, - const char *path, - struct opendal_bytes bytes); +struct opendal_error *opendal_operator_write(const struct opendal_operator *op, + const char *path, + struct opendal_bytes bytes); /** * \brief Blockingly read the data from `path`. * * Read the data out from `path` blockingly by operator. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The path you want to read the data out - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_result_read * @see opendal_error * @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated @@ -566,7 +708,7 @@ struct opendal_error *opendal_operator_blocking_write(const struct opendal_opera * ```C * // ... you have write "Hello, World!" to path "/testpath" * - * opendal_result_read r = opendal_operator_blocking_read(ptr, "testpath"); + * opendal_result_read r = opendal_operator_read(ptr, "testpath"); * assert(r.error == NULL); * * opendal_bytes *bytes = r.data; @@ -583,8 +725,8 @@ struct opendal_error *opendal_operator_blocking_write(const struct opendal_opera * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_read opendal_operator_blocking_read(const struct opendal_operator_ptr *ptr, - const char *path); +struct opendal_result_read opendal_operator_read(const struct opendal_operator *op, + const char *path); /** * \brief Blockingly read the data from `path`. @@ -592,11 +734,11 @@ struct opendal_result_read opendal_operator_blocking_read(const struct opendal_o * Read the data out from `path` blockingly by operator, returns * an opendal_result_read with error code. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The path you want to read the data out * @param buffer The buffer you want to read the data into * @param buffer_len The length of the buffer - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_result_read * @see opendal_code * @return Returns opendal_code @@ -608,14 +750,12 @@ struct opendal_result_read opendal_operator_blocking_read(const struct opendal_o * * Following is an example * ```C - * // ... you have write "Hello, World!" to path "/testpath" - * - * int length = 13; - * unsigned char buffer[length]; - * opendal_code r = opendal_operator_blocking_read_with_buffer(ptr, "testpath", buffer, length); - * assert(r == OPENDAL_OK); - * // assert buffer == "Hello, World!" + * // ... you have created an operator named op * + * opendal_result_operator_reader result = opendal_operator_reader(op, "/testpath"); + * assert(result.error == NULL); + * // The reader is in result.reader + * opendal_reader *reader = result.reader; * ``` * * # Safety @@ -628,7 +768,7 @@ struct opendal_result_read opendal_operator_blocking_read(const struct opendal_o * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_reader opendal_operator_blocking_reader(const struct opendal_operator_ptr *ptr, +struct opendal_result_operator_reader opendal_operator_reader(const struct opendal_operator *op, const char *path); /** @@ -637,9 +777,9 @@ struct opendal_result_reader opendal_operator_blocking_reader(const struct opend * Delete the object in `path` blockingly by `op_ptr`. * Error is NULL if successful, otherwise it contains the error code and error message. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The designated path you want to delete - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_error * @return NULL if succeeds, otherwise it contains the error code and error message. * @@ -647,17 +787,17 @@ struct opendal_result_reader opendal_operator_blocking_reader(const struct opend * * Following is an example * ```C - * //...prepare your opendal_operator_ptr, named ptr for example + * //...prepare your opendal_operator, named ptr for example * * // prepare your data * char* data = "Hello, World!"; * opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; - * opendal_error *error = opendal_operator_blocking_write(ptr, "/testpath", bytes); + * opendal_error *error = opendal_operator_write(ptr, "/testpath", bytes); * * assert(error == NULL); * * // now you can delete! - * opendal_error *error = opendal_operator_blocking_delete(ptr, "/testpath"); + * opendal_error *error = opendal_operator_delete(ptr, "/testpath"); * * // Assert that this succeeds * assert(error == NULL); @@ -673,8 +813,7 @@ struct opendal_result_reader opendal_operator_blocking_reader(const struct opend * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_error *opendal_operator_blocking_delete(const struct opendal_operator_ptr *ptr, - const char *path); +struct opendal_error *opendal_operator_delete(const struct opendal_operator *op, const char *path); /** * \brief Check whether the path exists. @@ -683,9 +822,9 @@ struct opendal_error *opendal_operator_blocking_delete(const struct opendal_oper * the error should be a nullptr. Otherwise, the field `is_exist` * is filled with false, and the error is set * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The path you want to check existence - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_result_is_exist * @see opendal_error * @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. @@ -715,7 +854,7 @@ struct opendal_error *opendal_operator_blocking_delete(const struct opendal_oper * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_operator_ptr *ptr, +struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_operator *op, const char *path); /** @@ -723,9 +862,9 @@ struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_op * * Error is NULL if successful, otherwise it contains the error code and error message. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The path you want to stat - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_result_stat * @see opendal_metadata * @return Returns opendal_result_stat, containing a metadata and an opendal_error. @@ -756,19 +895,19 @@ struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_op * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_ptr *ptr, +struct opendal_result_stat opendal_operator_stat(const struct opendal_operator *op, const char *path); /** * \brief Blockingly list the objects in `path`. * * List the object in `path` blockingly by `op_ptr`, return a result with a - * opendal_blocking_lister. Users should call opendal_lister_next() on the + * opendal_lister. Users should call opendal_lister_next() on the * lister. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The designated path you want to delete - * @see opendal_blocking_lister + * @see opendal_lister * @return Returns opendal_result_list, containing a lister and an opendal_error. * If the operation succeeds, the `lister` field would holds a valid lister and * the `error` field should hold nullptr. Otherwise the `lister`` will contain a @@ -779,11 +918,11 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * Following is an example * ```C * // You have written some data into some files path "root/dir1" - * // Your opendal_operator_ptr was called ptr - * opendal_result_list l = opendal_operator_blocking_list(ptr, "root/dir1"); + * // Your opendal_operator was called ptr + * opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); * assert(l.error == ERROR); * - * opendal_blocking_lister *lister = l.lister; + * opendal_lister *lister = l.lister; * opendal_list_entry *entry; * * while ((entry = opendal_lister_next(lister)) != NULL) { @@ -809,92 +948,13 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_list opendal_operator_blocking_list(const struct opendal_operator_ptr *ptr, - const char *path); - -/** - * \brief Frees the opendal_error, ok to call on NULL - */ -void opendal_error_free(struct opendal_error *ptr); - -/** - * \brief Free the heap-allocated operator pointed by opendal_operator_ptr. - * - * Please only use this for a pointer pointing at a valid opendal_operator_ptr. - * Calling this function on NULL does nothing, but calling this function on pointers - * of other type will lead to segfault. - * - * # Example - * - * ```C - * opendal_operator_ptr *ptr = opendal_operator_new("fs", NULL); - * // ... use this ptr, maybe some reads and writes - * - * // free this operator - * opendal_operator_free(ptr); - * ``` - */ -void opendal_operator_free(const struct opendal_operator_ptr *op); +struct opendal_result_list opendal_operator_list(const struct opendal_operator *op, + const char *path); /** * \brief Frees the heap memory used by the opendal_bytes */ -void opendal_bytes_free(struct opendal_bytes *ptr); - -/** - * \brief Free the heap-allocated metadata used by opendal_metadata - */ -void opendal_metadata_free(struct opendal_metadata *ptr); - -/** - * \brief Return the content_length of the metadata - * - * # Example - * ```C - * // ... previously you wrote "Hello, World!" to path "/testpath" - * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - * assert(s.error == NULL); - * - * opendal_metadata *meta = s.meta; - * assert(opendal_metadata_content_length(meta) == 13); - * ``` - */ -uint64_t opendal_metadata_content_length(const struct opendal_metadata *self); - -/** - * \brief Return whether the path represents a file - * - * # Example - * ```C - * // ... previously you wrote "Hello, World!" to path "/testpath" - * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - * assert(s.error == NULL); - * - * opendal_metadata *meta = s.meta; - * assert(opendal_metadata_is_file(meta)); - * ``` - */ -bool opendal_metadata_is_file(const struct opendal_metadata *self); - -/** - * \brief Return whether the path represents a directory - * - * # Example - * ```C - * // ... previously you wrote "Hello, World!" to path "/testpath" - * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - * assert(s.error == NULL); - * - * opendal_metadata *meta = s.meta; - * - * // this is not a directory - * assert(!opendal_metadata_is_dir(meta)); - * ``` - * - * \todo This is not a very clear example. A clearer example will be added - * after we support opendal_operator_mkdir() - */ -bool opendal_metadata_is_dir(const struct opendal_metadata *self); +void opendal_bytes_free(struct opendal_bytes *bs); /** * \brief Construct a heap-allocated opendal_operator_options @@ -934,22 +994,6 @@ void opendal_operator_options_set(struct opendal_operator_options *self, */ void opendal_operator_options_free(const struct opendal_operator_options *options); -/** - * \brief Return the next object to be listed - * - * Lister is an iterator of the objects under its path, this method is the same as - * calling next() on the iterator - * - * For examples, please see the comment section of opendal_operator_blocking_list() - * @see opendal_operator_blocking_list() - */ -struct opendal_list_entry *opendal_lister_next(const struct opendal_blocking_lister *self); - -/** - * \brief Free the heap-allocated metadata used by opendal_blocking_lister - */ -void opendal_lister_free(const struct opendal_blocking_lister *p); - /** * \brief Path of entry. * @@ -957,7 +1001,7 @@ void opendal_lister_free(const struct opendal_blocking_lister *p); * * \note To free the string, you can directly call free() */ -char *opendal_list_entry_path(const struct opendal_list_entry *self); +char *opendal_entry_path(const struct opendal_entry *self); /** * \brief Name of entry. @@ -968,17 +1012,23 @@ char *opendal_list_entry_path(const struct opendal_list_entry *self); * * \note To free the string, you can directly call free() */ -char *opendal_list_entry_name(const struct opendal_list_entry *self); +char *opendal_entry_name(const struct opendal_entry *self); /** * \brief Frees the heap memory used by the opendal_list_entry */ -void opendal_list_entry_free(struct opendal_list_entry *ptr); +void opendal_entry_free(struct opendal_entry *ptr); -struct opendal_result_reader_read opendal_reader_read(const struct opendal_reader *self, +/** + * \brief Read data from the reader. + */ +struct opendal_result_reader_read opendal_reader_read(const struct opendal_reader *reader, uint8_t *buf, uintptr_t len); +/** + * \brief Frees the heap memory used by the opendal_reader. + */ void opendal_reader_free(struct opendal_reader *ptr); #ifdef __cplusplus diff --git a/bindings/c/src/entry.rs b/bindings/c/src/entry.rs new file mode 100644 index 000000000000..44e9c2f19ad6 --- /dev/null +++ b/bindings/c/src/entry.rs @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use ::opendal as core; +use std::ffi::CString; +use std::os::raw::c_char; + +/// \brief opendal_list_entry is the entry under a path, which is listed from the opendal_lister +/// +/// For examples, please see the comment section of opendal_operator_list() +/// @see opendal_operator_list() +/// @see opendal_list_entry_path() +/// @see opendal_list_entry_name() +#[repr(C)] +pub struct opendal_entry { + inner: *mut core::Entry, +} + +impl opendal_entry { + /// Used to convert the Rust type into C type + pub(crate) fn new(entry: core::Entry) -> Self { + Self { + inner: Box::into_raw(Box::new(entry)), + } + } + + /// \brief Path of entry. + /// + /// Path is relative to operator's root. Only valid in current operator. + /// + /// \note To free the string, you can directly call free() + #[no_mangle] + pub unsafe extern "C" fn opendal_entry_path(&self) -> *mut c_char { + let s = (*self.inner).path(); + let c_str = CString::new(s).unwrap(); + c_str.into_raw() + } + + /// \brief Name of entry. + /// + /// Name is the last segment of path. + /// If this entry is a dir, `Name` MUST endswith `/` + /// Otherwise, `Name` MUST NOT endswith `/`. + /// + /// \note To free the string, you can directly call free() + #[no_mangle] + pub unsafe extern "C" fn opendal_entry_name(&self) -> *mut c_char { + let s = (*self.inner).name(); + let c_str = CString::new(s).unwrap(); + c_str.into_raw() + } + + /// \brief Frees the heap memory used by the opendal_list_entry + #[no_mangle] + pub unsafe extern "C" fn opendal_entry_free(ptr: *mut opendal_entry) { + if !ptr.is_null() { + let _ = unsafe { Box::from_raw((*ptr).inner) }; + let _ = unsafe { Box::from_raw(ptr) }; + } + } +} diff --git a/bindings/c/src/error.rs b/bindings/c/src/error.rs index 366d646ae0a4..359984620161 100644 --- a/bindings/c/src/error.rs +++ b/bindings/c/src/error.rs @@ -15,21 +15,15 @@ // specific language governing permissions and limitations // under the License. -use ::opendal as od; +use ::opendal as core; use crate::types::opendal_bytes; -/// \brief The wrapper type for opendal's Rust core error, wrapped because of the -/// orphan rule. -/// -/// \note User should never use this type directly, use [`opendal_error`] instead. -struct raw_error(od::Error); - /// \brief The error code for all opendal APIs in C binding. /// \todo The error handling is not complete, the error with error message will be /// added in the future. #[repr(C)] -pub(crate) enum opendal_code { +pub enum opendal_code { /// returning it back. For example, s3 returns an internal service error. OPENDAL_UNEXPECTED, /// Underlying service doesn't support this operation. @@ -52,21 +46,19 @@ pub(crate) enum opendal_code { OPENDAL_IS_SAME_FILE, } -impl raw_error { - /// Convert the [`od::ErrorKind`] of [`od::Error`] to [`opendal_code`] - pub(crate) fn error_code(&self) -> opendal_code { - let e = &self.0; - match e.kind() { - od::ErrorKind::Unexpected => opendal_code::OPENDAL_UNEXPECTED, - od::ErrorKind::Unsupported => opendal_code::OPENDAL_UNSUPPORTED, - od::ErrorKind::ConfigInvalid => opendal_code::OPENDAL_CONFIG_INVALID, - od::ErrorKind::NotFound => opendal_code::OPENDAL_NOT_FOUND, - od::ErrorKind::PermissionDenied => opendal_code::OPENDAL_PERMISSION_DENIED, - od::ErrorKind::IsADirectory => opendal_code::OPENDAL_IS_A_DIRECTORY, - od::ErrorKind::NotADirectory => opendal_code::OPENDAL_NOT_A_DIRECTORY, - od::ErrorKind::AlreadyExists => opendal_code::OPENDAL_ALREADY_EXISTS, - od::ErrorKind::RateLimited => opendal_code::OPENDAL_RATE_LIMITED, - od::ErrorKind::IsSameFile => opendal_code::OPENDAL_IS_SAME_FILE, +impl From for opendal_code { + fn from(value: core::ErrorKind) -> Self { + match value { + core::ErrorKind::Unexpected => opendal_code::OPENDAL_UNEXPECTED, + core::ErrorKind::Unsupported => opendal_code::OPENDAL_UNSUPPORTED, + core::ErrorKind::ConfigInvalid => opendal_code::OPENDAL_CONFIG_INVALID, + core::ErrorKind::NotFound => opendal_code::OPENDAL_NOT_FOUND, + core::ErrorKind::PermissionDenied => opendal_code::OPENDAL_PERMISSION_DENIED, + core::ErrorKind::IsADirectory => opendal_code::OPENDAL_IS_A_DIRECTORY, + core::ErrorKind::NotADirectory => opendal_code::OPENDAL_NOT_A_DIRECTORY, + core::ErrorKind::AlreadyExists => opendal_code::OPENDAL_ALREADY_EXISTS, + core::ErrorKind::RateLimited => opendal_code::OPENDAL_RATE_LIMITED, + core::ErrorKind::IsSameFile => opendal_code::OPENDAL_IS_SAME_FILE, // if this is triggered, check the [`core`] crate and add a // new error code accordingly _ => panic!("The newly added ErrorKind in core crate is not handled in C bindings"), @@ -98,19 +90,15 @@ pub struct opendal_error { } impl opendal_error { - // The caller should sink the error to heap memory and return the pointer - // that will not be freed by rustc - pub(crate) fn from_opendal_error(error: od::Error) -> Self { - let error = raw_error(error); - let code = error.error_code(); - let c_str = format!("{}", error.0); - let message = opendal_bytes::new(c_str.into_bytes()); - opendal_error { code, message } - } + /// Create a new opendal error via `core::Error`. + /// + /// We will call `Box::leak()` to leak this error, so the caller should be responsible for + /// free this error. + pub fn new(err: core::Error) -> *mut opendal_error { + let code = opendal_code::from(err.kind()); + let message = opendal_bytes::new(err.to_string().into_bytes()); - pub(crate) fn manual_error(code: opendal_code, message: String) -> Self { - let message = opendal_bytes::new(message.into_bytes()); - opendal_error { code, message } + Box::into_raw(Box::new(opendal_error { code, message })) } /// \brief Frees the opendal_error, ok to call on NULL diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 79abaeda5b55..76f26381ac75 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -16,7 +16,12 @@ // under the License. #![warn(missing_docs)] +// This crate is the C binding for the OpenDAL project. +// So it's type node can't meet camel case. #![allow(non_camel_case_types)] +// This crate is the C binding for the OpenDAL project. +// Nearly all the functions exposed to C FFI are unsafe. +#![allow(clippy::missing_safety_doc)] //! The OpenDAL C binding. //! @@ -26,596 +31,34 @@ //! For examples, you may see the examples subdirectory mod error; -mod result; -mod types; - -use std::collections::HashMap; -use std::os::raw::c_char; -use std::str::FromStr; - -use ::opendal as od; -use error::opendal_error; -use once_cell::sync::Lazy; -use result::opendal_result_list; -use result::opendal_result_operator_new; -use result::opendal_result_reader; -use types::opendal_blocking_lister; -use types::opendal_reader; - -use crate::result::opendal_result_is_exist; -use crate::result::opendal_result_read; -use crate::result::opendal_result_stat; -use crate::types::opendal_bytes; -use crate::types::opendal_metadata; -use crate::types::opendal_operator_options; -use crate::types::opendal_operator_ptr; - -static RUNTIME: Lazy = Lazy::new(|| { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() -}); - -fn build_operator(schema: od::Scheme, map: HashMap) -> od::Result { - let mut op = match od::Operator::via_map(schema, map) { - Ok(o) => o, - Err(e) => return Err(e), - }; - if !op.info().full_capability().blocking { - let runtime = - tokio::runtime::Handle::try_current().unwrap_or_else(|_| RUNTIME.handle().clone()); - let _guard = runtime.enter(); - op = op.layer(od::layers::BlockingLayer::create().expect("blocking layer must be created")); - } - Ok(op) -} -/// \brief Construct an operator based on `scheme` and `options` -/// -/// Uses an array of key-value pairs to initialize the operator based on provided `scheme` -/// and `options`. For each scheme, i.e. Backend, different options could be set, you may -/// reference the [documentation](https://opendal.apache.org/docs/category/services/) for -/// each service, especially for the **Configuration Part**. -/// -/// @param scheme the service scheme you want to specify, e.g. "fs", "s3", "supabase" -/// @param options the pointer to the options for this operators, it could be NULL, which means no -/// option is set -/// @see opendal_operator_options -/// @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction -/// succeeds. On success the operator_ptr field is a valid pointer to a newly allocated opendal_operator_ptr, -/// and the error field is NULL. Otherwise, the operator_ptr field is a NULL pointer and the error field. -/// -/// # Example -/// -/// Following is an example. -/// ```C -/// // Allocate a new options -/// opendal_operator_options *options = opendal_operator_options_new(); -/// // Set the options you need -/// opendal_operator_options_set(options, "root", "/myroot"); -/// -/// // Construct the operator based on the options and scheme -/// opendal_result_operator_new result = opendal_operator_new("memory", options); -/// opendal_operator_ptr* op = result.operator_ptr; -/// -/// // you could free the options right away since the options is not used afterwards -/// opendal_operator_options_free(options); -/// -/// // ... your operations -/// ``` -/// -/// # Safety -/// -/// The only unsafe case is passing a invalid c string pointer to the `scheme` argument. -#[no_mangle] -pub unsafe extern "C" fn opendal_operator_new( - scheme: *const c_char, - options: *const opendal_operator_options, -) -> opendal_result_operator_new { - if scheme.is_null() { - let error = opendal_error::manual_error( - error::opendal_code::OPENDAL_CONFIG_INVALID, - "The scheme given is pointing at NULL".into(), - ); - let result = opendal_result_operator_new { - operator_ptr: std::ptr::null_mut(), - error: Box::into_raw(Box::new(error)), - }; - return result; - } - - let scheme_str = unsafe { std::ffi::CStr::from_ptr(scheme).to_str().unwrap() }; - let scheme = match od::Scheme::from_str(scheme_str) { - Ok(s) => s, - Err(e) => { - let e = opendal_error::from_opendal_error(e); - let result = opendal_result_operator_new { - operator_ptr: std::ptr::null_mut(), - error: Box::into_raw(Box::new(e)), - }; - return result; - } - }; - - let mut map = HashMap::default(); - if !options.is_null() { - for (k, v) in (*options).as_ref() { - map.insert(k.to_string(), v.to_string()); - } - } - - match build_operator(scheme, map) { - Ok(op) => { - let op = opendal_operator_ptr::from(Box::into_raw(Box::new(op.blocking()))); - opendal_result_operator_new { - operator_ptr: Box::into_raw(Box::new(op)), - error: std::ptr::null_mut(), - } - } - Err(e) => { - let e = opendal_error::from_opendal_error(e); - opendal_result_operator_new { - operator_ptr: std::ptr::null_mut(), - error: Box::into_raw(Box::new(e)), - } - } - } -} +pub use error::opendal_code; +pub use error::opendal_error; -/// \brief Blockingly write raw bytes to `path`. -/// -/// Write the `bytes` into the `path` blockingly by `op_ptr`. -/// Error is NULL if successful, otherwise it contains the error code and error message. -/// -/// \note It is important to notice that the `bytes` that is passes in will be consumed by this -/// function. Therefore, you should not use the `bytes` after this function returns. -/// -/// @param ptr The opendal_operator_ptr created previously -/// @param path The designated path you want to write your bytes in -/// @param bytes The opendal_byte typed bytes to be written -/// @see opendal_operator_ptr -/// @see opendal_bytes -/// @see opendal_error -/// @return NULL if succeeds, otherwise it contains the error code and error message. -/// -/// # Example -/// -/// Following is an example -/// ```C -/// //...prepare your opendal_operator_ptr, named ptr for example -/// -/// // prepare your data -/// char* data = "Hello, World!"; -/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; -/// -/// // now you can write! -/// opendal_error *err = opendal_operator_blocking_write(ptr, "/testpath", bytes); -/// -/// // Assert that this succeeds -/// assert(err == NULL); -/// ``` -/// -/// # 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. -/// * The `bytes` provided has valid byte in the `data` field and the `len` field is set -/// correctly. -/// -/// # Panic -/// -/// * If the `path` points to NULL, this function panics, i.e. exits with information -#[no_mangle] -pub unsafe extern "C" fn opendal_operator_blocking_write( - ptr: *const opendal_operator_ptr, - path: *const c_char, - bytes: opendal_bytes, -) -> *mut opendal_error { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } +mod lister; +pub use lister::opendal_lister; - let op = (*ptr).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.write(path, bytes) { - Ok(_) => std::ptr::null_mut(), - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - Box::into_raw(e) - } - } -} +mod metadata; +pub use metadata::opendal_metadata; -/// \brief Blockingly read the data from `path`. -/// -/// Read the data out from `path` blockingly by operator. -/// -/// @param ptr The opendal_operator_ptr created previously -/// @param path The path you want to read the data out -/// @see opendal_operator_ptr -/// @see opendal_result_read -/// @see opendal_error -/// @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated -/// opendal_bytes, the `error` field contains the error. If the `error` is not NULL, then -/// the operation failed and the `data` field is a nullptr. -/// -/// \note If the read operation succeeds, the returned opendal_bytes is newly allocated on heap. -/// After your usage of that, please call opendal_bytes_free() to free the space. -/// -/// # Example -/// -/// Following is an example -/// ```C -/// // ... you have write "Hello, World!" to path "/testpath" -/// -/// opendal_result_read r = opendal_operator_blocking_read(ptr, "testpath"); -/// assert(r.error == NULL); -/// -/// opendal_bytes *bytes = r.data; -/// assert(bytes->len == 13); -/// ``` -/// -/// # 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_blocking_read( - ptr: *const opendal_operator_ptr, - path: *const c_char, -) -> opendal_result_read { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } +mod operator; +pub use operator::opendal_operator; - let op = (*ptr).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - let data = op.read(path); - match data { - Ok(d) => { - let v = Box::new(opendal_bytes::new(d)); - opendal_result_read { - data: Box::into_raw(v), - error: std::ptr::null_mut(), - } - } - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_read { - data: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } - } -} - -/// \brief Blockingly read the data from `path`. -/// -/// Read the data out from `path` blockingly by operator, returns -/// an opendal_result_read with error code. -/// -/// @param ptr The opendal_operator_ptr created previously -/// @param path The path you want to read the data out -/// @param buffer The buffer you want to read the data into -/// @param buffer_len The length of the buffer -/// @see opendal_operator_ptr -/// @see opendal_result_read -/// @see opendal_code -/// @return Returns opendal_code -/// -/// \note If the read operation succeeds, the returned opendal_bytes is newly allocated on heap. -/// After your usage of that, please call opendal_bytes_free() to free the space. -/// -/// # Example -/// -/// Following is an example -/// ```C -/// // ... you have write "Hello, World!" to path "/testpath" -/// -/// int length = 13; -/// unsigned char buffer[length]; -/// opendal_code r = opendal_operator_blocking_read_with_buffer(ptr, "testpath", buffer, length); -/// assert(r == OPENDAL_OK); -/// // assert buffer == "Hello, World!" -/// -/// ``` -/// -/// # 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_blocking_reader( - ptr: *const opendal_operator_ptr, - path: *const c_char, -) -> opendal_result_reader { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } - let op = (*ptr).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.reader(path) { - Ok(reader) => opendal_result_reader { - reader: Box::into_raw(Box::new(opendal_reader::new(reader))), - error: std::ptr::null_mut(), - }, - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_reader { - reader: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } - } -} - -/// \brief Blockingly delete the object in `path`. -/// -/// Delete the object in `path` blockingly by `op_ptr`. -/// Error is NULL if successful, otherwise it contains the error code and error message. -/// -/// @param ptr The opendal_operator_ptr created previously -/// @param path The designated path you want to delete -/// @see opendal_operator_ptr -/// @see opendal_error -/// @return NULL if succeeds, otherwise it contains the error code and error message. -/// -/// # Example -/// -/// Following is an example -/// ```C -/// //...prepare your opendal_operator_ptr, named ptr for example -/// -/// // prepare your data -/// char* data = "Hello, World!"; -/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; -/// opendal_error *error = opendal_operator_blocking_write(ptr, "/testpath", bytes); -/// -/// assert(error == NULL); -/// -/// // now you can delete! -/// opendal_error *error = opendal_operator_blocking_delete(ptr, "/testpath"); -/// -/// // Assert that this succeeds -/// assert(error == NULL); -/// ``` -/// -/// # 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_blocking_delete( - ptr: *const opendal_operator_ptr, - path: *const c_char, -) -> *mut opendal_error { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } - - let op = (*ptr).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.delete(path) { - Ok(_) => std::ptr::null_mut(), - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - Box::into_raw(e) - } - } -} - -/// \brief Check whether the path exists. -/// -/// If the operation succeeds, no matter the path exists or not, -/// the error should be a nullptr. Otherwise, the field `is_exist` -/// is filled with false, and the error is set -/// -/// @param ptr The opendal_operator_ptr created previously -/// @param path The path you want to check existence -/// @see opendal_operator_ptr -/// @see opendal_result_is_exist -/// @see opendal_error -/// @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. -/// However, it the operation fails, the `is_exist` will contains false and the error will be set. -/// -/// # Example -/// -/// ```C -/// // .. you previously wrote some data to path "/mytest/obj" -/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/mytest/obj"); -/// assert(e.error == NULL); -/// assert(e.is_exist); -/// -/// // but you previously did **not** write any data to path "/yourtest/obj" -/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/yourtest/obj"); -/// assert(e.error == NULL); -/// assert(!e.is_exist); -/// ``` -/// -/// # 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_is_exist( - ptr: *const 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 = (*ptr).as_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, - error: std::ptr::null_mut(), - }, - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_is_exist { - is_exist: false, - error: Box::into_raw(e), - } - } - } -} - -/// \brief Stat the path, return its metadata. -/// -/// Error is NULL if successful, otherwise it contains the error code and error message. -/// -/// @param ptr The opendal_operator_ptr created previously -/// @param path The path you want to stat -/// @see opendal_operator_ptr -/// @see opendal_result_stat -/// @see opendal_metadata -/// @return Returns opendal_result_stat, containing a metadata and an opendal_error. -/// If the operation succeeds, the `meta` field would holds a valid metadata and -/// the `error` field should hold nullptr. Otherwise the metadata will contain a -/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. -/// -/// # Example -/// -/// ```C -/// // ... previously you wrote "Hello, World!" to path "/testpath" -/// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); -/// assert(s.error == NULL); -/// -/// const opendal_metadata *meta = s.meta; -/// -/// // ... you could now use your metadata, notice that please only access metadata -/// // using the APIs provided by OpenDAL -/// ``` -/// -/// # 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_stat( - ptr: *const opendal_operator_ptr, - path: *const c_char, -) -> opendal_result_stat { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } - - let op = (*ptr).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.stat(path) { - Ok(m) => opendal_result_stat { - meta: Box::into_raw(Box::new(opendal_metadata::new(m))), - error: std::ptr::null_mut(), - }, - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_stat { - meta: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } - } -} +mod result; +pub use result::opendal_result_is_exist; +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_read; +pub use result::opendal_result_reader_read; +pub use result::opendal_result_stat; -/// \brief Blockingly list the objects in `path`. -/// -/// List the object in `path` blockingly by `op_ptr`, return a result with a -/// opendal_blocking_lister. Users should call opendal_lister_next() on the -/// lister. -/// -/// @param ptr The opendal_operator_ptr created previously -/// @param path The designated path you want to delete -/// @see opendal_blocking_lister -/// @return Returns opendal_result_list, containing a lister and an opendal_error. -/// If the operation succeeds, the `lister` field would holds a valid lister and -/// the `error` field should hold nullptr. Otherwise the `lister`` will contain a -/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. -/// -/// # Example -/// -/// Following is an example -/// ```C -/// // You have written some data into some files path "root/dir1" -/// // Your opendal_operator_ptr was called ptr -/// opendal_result_list l = opendal_operator_blocking_list(ptr, "root/dir1"); -/// assert(l.error == ERROR); -/// -/// opendal_blocking_lister *lister = l.lister; -/// opendal_list_entry *entry; -/// -/// while ((entry = opendal_lister_next(lister)) != NULL) { -/// const char* de_path = opendal_list_entry_path(entry); -/// const char* de_name = opendal_list_entry_name(entry); -/// // ...... your operations -/// -/// // remember to free the entry after you are done using it -/// opendal_list_entry_free(entry); -/// } -/// -/// // and remember to free the lister -/// opendal_lister_free(lister); -/// ``` -/// -/// # 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_blocking_list( - ptr: *const opendal_operator_ptr, - path: *const c_char, -) -> opendal_result_list { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } +mod types; +pub use types::opendal_bytes; +pub use types::opendal_operator_options; - let op = (*ptr).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.lister(path) { - Ok(lister) => opendal_result_list { - lister: Box::into_raw(Box::new(opendal_blocking_lister::new(lister))), - error: std::ptr::null_mut(), - }, +mod entry; +pub use entry::opendal_entry; - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_list { - lister: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } - } -} +mod reader; +pub use reader::opendal_reader; diff --git a/bindings/c/src/lister.rs b/bindings/c/src/lister.rs new file mode 100644 index 000000000000..08f787480e3f --- /dev/null +++ b/bindings/c/src/lister.rs @@ -0,0 +1,81 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use ::opendal as core; + +use super::*; + +/// \brief BlockingLister is designed to list entries at given path in a blocking +/// manner. +/// +/// Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) +/// +/// For examples, please see the comment section of opendal_operator_list() +/// @see opendal_operator_list() +#[repr(C)] +pub struct opendal_lister { + inner: *mut core::BlockingLister, +} + +impl opendal_lister { + pub(crate) fn new(lister: core::BlockingLister) -> Self { + Self { + inner: Box::into_raw(Box::new(lister)), + } + } + + /// \brief Return the next object to be listed + /// + /// Lister is an iterator of the objects under its path, this method is the same as + /// calling next() on the iterator + /// + /// For examples, please see the comment section of opendal_operator_list() + /// @see opendal_operator_list() + #[no_mangle] + pub unsafe extern "C" fn opendal_lister_next(&self) -> opendal_result_lister_next { + let e = (*self.inner).next(); + if e.is_none() { + return opendal_result_lister_next { + entry: std::ptr::null_mut(), + error: std::ptr::null_mut(), + }; + } + + match e.unwrap() { + Ok(e) => { + let ent = Box::into_raw(Box::new(opendal_entry::new(e))); + opendal_result_lister_next { + entry: ent, + error: std::ptr::null_mut(), + } + } + Err(e) => opendal_result_lister_next { + entry: std::ptr::null_mut(), + error: opendal_error::new(e), + }, + } + } + + /// \brief Free the heap-allocated metadata used by opendal_lister + #[no_mangle] + pub unsafe extern "C" fn opendal_lister_free(p: *const opendal_lister) { + unsafe { + let _ = Box::from_raw((*p).inner); + let _ = Box::from_raw(p as *mut opendal_lister); + } + } +} diff --git a/bindings/c/src/metadata.rs b/bindings/c/src/metadata.rs new file mode 100644 index 000000000000..15c38bbd2cf7 --- /dev/null +++ b/bindings/c/src/metadata.rs @@ -0,0 +1,114 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use ::opendal as core; + +/// \brief Carries all metadata associated with a path. +/// +/// The metadata of the "thing" under a path. Please **only** use the opendal_metadata +/// with our provided API, e.g. opendal_metadata_content_length(). +/// +/// \note The metadata is also heap-allocated, please call opendal_metadata_free() on this +/// to free the heap memory. +/// +/// @see opendal_metadata_free +#[repr(C)] +pub struct opendal_metadata { + /// The pointer to the opendal::Metadata in the Rust code. + /// Only touch this on judging whether it is NULL. + pub inner: *mut core::Metadata, +} + +impl opendal_metadata { + /// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible + /// [`opendal_metadata`] + pub(crate) fn new(m: core::Metadata) -> Self { + Self { + inner: Box::into_raw(Box::new(m)), + } + } + + /// \brief Free the heap-allocated metadata used by opendal_metadata + #[no_mangle] + pub unsafe extern "C" fn opendal_metadata_free(ptr: *mut opendal_metadata) { + unsafe { + let _ = Box::from_raw((*ptr).inner); + let _ = Box::from_raw(ptr); + } + } + + /// \brief Return the content_length of the metadata + /// + /// # Example + /// ```C + /// // ... previously you wrote "Hello, World!" to path "/testpath" + /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + /// assert(s.error == NULL); + /// + /// opendal_metadata *meta = s.meta; + /// assert(opendal_metadata_content_length(meta) == 13); + /// ``` + #[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() } + } + + /// \brief Return whether the path represents a file + /// + /// # Example + /// ```C + /// // ... previously you wrote "Hello, World!" to path "/testpath" + /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + /// assert(s.error == NULL); + /// + /// opendal_metadata *meta = s.meta; + /// assert(opendal_metadata_is_file(meta)); + /// ``` + #[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 + let m = unsafe { &*self.inner }; + + m.is_file() + } + + /// \brief Return whether the path represents a directory + /// + /// # Example + /// ```C + /// // ... previously you wrote "Hello, World!" to path "/testpath" + /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + /// assert(s.error == NULL); + /// + /// opendal_metadata *meta = s.meta; + /// + /// // this is not a directory + /// assert(!opendal_metadata_is_dir(meta)); + /// ``` + /// + /// \todo This is not a very clear example. A clearer example will be added + /// after we support opendal_operator_mkdir() + #[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() } + } +} diff --git a/bindings/c/src/operator.rs b/bindings/c/src/operator.rs new file mode 100644 index 000000000000..4f6406236a38 --- /dev/null +++ b/bindings/c/src/operator.rs @@ -0,0 +1,630 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use std::collections::HashMap; +use std::os::raw::c_char; +use std::str::FromStr; + +use ::opendal as core; +use once_cell::sync::Lazy; + +use super::*; + +static RUNTIME: Lazy = Lazy::new(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() +}); + +/// \brief Used to access almost all OpenDAL APIs. It represents a +/// operator that provides the unified interfaces provided by OpenDAL. +/// +/// @see opendal_operator_new This function construct the operator +/// @see opendal_operator_free This function frees the heap memory of the operator +/// +/// \note The opendal_operator actually owns a pointer to +/// a opendal::BlockingOperator, which is inside the Rust core code. +/// +/// \remark You may use the field `ptr` to check whether this is a NULL +/// operator. +#[repr(C)] +pub struct opendal_operator { + /// The pointer to the opendal::BlockingOperator in the Rust code. + /// Only touch this on judging whether it is NULL. + ptr: *const core::BlockingOperator, +} + +impl opendal_operator { + /// \brief Free the heap-allocated operator pointed by opendal_operator. + /// + /// Please only use this for a pointer pointing at a valid opendal_operator. + /// Calling this function on NULL does nothing, but calling this function on pointers + /// of other type will lead to segfault. + /// + /// # Example + /// + /// ```C + /// opendal_operator *ptr = opendal_operator_new("fs", NULL); + /// // ... use this ptr, maybe some reads and writes + /// + /// // free this operator + /// opendal_operator_free(ptr); + /// ``` + #[no_mangle] + pub unsafe extern "C" fn opendal_operator_free(op: *const opendal_operator) { + let _ = unsafe { Box::from_raw((*op).ptr as *mut core::BlockingOperator) }; + let _ = unsafe { Box::from_raw(op as *mut opendal_operator) }; + } +} + +/// Returns a reference to the underlying [`od::BlockingOperator`] +/// +/// # Safety +/// +/// The ptr is a raw pointer to the underlying [`od::BlockingOperator`]. +/// It will only free when opendal_operator_free has been called. +impl AsRef for opendal_operator { + fn as_ref(&self) -> &core::BlockingOperator { + unsafe { &*(self.ptr) } + } +} + +#[allow(clippy::from_over_into)] +impl From<*const core::BlockingOperator> for opendal_operator { + fn from(value: *const core::BlockingOperator) -> Self { + Self { ptr: value } + } +} + +#[allow(clippy::from_over_into)] +impl From<*mut core::BlockingOperator> for opendal_operator { + fn from(value: *mut core::BlockingOperator) -> Self { + Self { ptr: value } + } +} + +fn build_operator( + schema: core::Scheme, + map: HashMap, +) -> core::Result { + let mut op = match core::Operator::via_map(schema, map) { + Ok(o) => o, + Err(e) => return Err(e), + }; + if !op.info().full_capability().blocking { + let runtime = + tokio::runtime::Handle::try_current().unwrap_or_else(|_| RUNTIME.handle().clone()); + let _guard = runtime.enter(); + op = op + .layer(core::layers::BlockingLayer::create().expect("blocking layer must be created")); + } + Ok(op) +} + +/// \brief Construct an operator based on `scheme` and `options` +/// +/// Uses an array of key-value pairs to initialize the operator based on provided `scheme` +/// and `options`. For each scheme, i.e. Backend, different options could be set, you may +/// reference the [documentation](https://opendal.apache.org/docs/category/services/) for +/// each service, especially for the **Configuration Part**. +/// +/// @param scheme the service scheme you want to specify, e.g. "fs", "s3", "supabase" +/// @param options the pointer to the options for this operators, it could be NULL, which means no +/// option is set +/// @see opendal_operator_options +/// @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction +/// succeeds. On success the operator field is a valid pointer to a newly allocated opendal_operator, +/// and the error field is NULL. Otherwise, the operator field is a NULL pointer and the error field. +/// +/// # Example +/// +/// Following is an example. +/// ```C +/// // Allocate a new options +/// opendal_operator_options *options = opendal_operator_options_new(); +/// // Set the options you need +/// opendal_operator_options_set(options, "root", "/myroot"); +/// +/// // Construct the operator based on the options and scheme +/// opendal_result_operator_new result = opendal_operator_new("memory", options); +/// opendal_operator* op = result.op; +/// +/// // you could free the options right away since the options is not used afterwards +/// opendal_operator_options_free(options); +/// +/// // ... your operations +/// ``` +/// +/// # Safety +/// +/// The only unsafe case is passing a invalid c string pointer to the `scheme` argument. +#[no_mangle] +pub unsafe extern "C" fn opendal_operator_new( + scheme: *const c_char, + options: *const opendal_operator_options, +) -> opendal_result_operator_new { + if scheme.is_null() { + panic!("The scheme given is pointing at NULL"); + } + + let scheme_str = unsafe { std::ffi::CStr::from_ptr(scheme).to_str().unwrap() }; + let scheme = match core::Scheme::from_str(scheme_str) { + Ok(s) => s, + Err(e) => { + return opendal_result_operator_new { + op: std::ptr::null_mut(), + error: opendal_error::new(e), + }; + } + }; + + let mut map = HashMap::default(); + if !options.is_null() { + for (k, v) in (*options).as_ref() { + map.insert(k.to_string(), v.to_string()); + } + } + + match build_operator(scheme, map) { + Ok(op) => { + let op = opendal_operator::from(Box::into_raw(Box::new(op.blocking()))); + opendal_result_operator_new { + op: Box::into_raw(Box::new(op)), + error: std::ptr::null_mut(), + } + } + Err(e) => opendal_result_operator_new { + op: std::ptr::null_mut(), + error: opendal_error::new(e), + }, + } +} + +/// \brief Blockingly write raw bytes to `path`. +/// +/// Write the `bytes` into the `path` blockingly by `op_ptr`. +/// Error is NULL if successful, otherwise it contains the error code and error message. +/// +/// \note It is important to notice that the `bytes` that is passes in will be consumed by this +/// function. Therefore, you should not use the `bytes` after this function returns. +/// +/// @param ptr The opendal_operator created previously +/// @param path The designated path you want to write your bytes in +/// @param bytes The opendal_byte typed bytes to be written +/// @see opendal_operator +/// @see opendal_bytes +/// @see opendal_error +/// @return NULL if succeeds, otherwise it contains the error code and error message. +/// +/// # Example +/// +/// Following is an example +/// ```C +/// //...prepare your opendal_operator, named ptr for example +/// +/// // prepare your data +/// char* data = "Hello, World!"; +/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; +/// +/// // now you can write! +/// opendal_error *err = opendal_operator_write(ptr, "/testpath", bytes); +/// +/// // Assert that this succeeds +/// assert(err == NULL); +/// ``` +/// +/// # 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. +/// * The `bytes` provided has valid byte in the `data` field and the `len` field is set +/// correctly. +/// +/// # Panic +/// +/// * If the `path` points to NULL, this function panics, i.e. exits with information +#[no_mangle] +pub unsafe extern "C" fn opendal_operator_write( + op: *const opendal_operator, + path: *const c_char, + bytes: opendal_bytes, +) -> *mut opendal_error { + 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() }; + match op.write(path, bytes) { + Ok(_) => std::ptr::null_mut(), + Err(e) => opendal_error::new(e), + } +} + +/// \brief Blockingly read the data from `path`. +/// +/// Read the data out from `path` blockingly by operator. +/// +/// @param ptr The opendal_operator created previously +/// @param path The path you want to read the data out +/// @see opendal_operator +/// @see opendal_result_read +/// @see opendal_error +/// @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated +/// opendal_bytes, the `error` field contains the error. If the `error` is not NULL, then +/// the operation failed and the `data` field is a nullptr. +/// +/// \note If the read operation succeeds, the returned opendal_bytes is newly allocated on heap. +/// After your usage of that, please call opendal_bytes_free() to free the space. +/// +/// # Example +/// +/// Following is an example +/// ```C +/// // ... you have write "Hello, World!" to path "/testpath" +/// +/// opendal_result_read r = opendal_operator_read(ptr, "testpath"); +/// assert(r.error == NULL); +/// +/// opendal_bytes *bytes = r.data; +/// assert(bytes->len == 13); +/// ``` +/// +/// # 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_read( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_read { + 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 data = op.read(path); + match data { + Ok(d) => { + let v = Box::new(opendal_bytes::new(d)); + opendal_result_read { + data: Box::into_raw(v), + error: std::ptr::null_mut(), + } + } + Err(e) => opendal_result_read { + data: std::ptr::null_mut(), + error: opendal_error::new(e), + }, + } +} + +/// \brief Blockingly read the data from `path`. +/// +/// Read the data out from `path` blockingly by operator, returns +/// an opendal_result_read with error code. +/// +/// @param ptr The opendal_operator created previously +/// @param path The path you want to read the data out +/// @param buffer The buffer you want to read the data into +/// @param buffer_len The length of the buffer +/// @see opendal_operator +/// @see opendal_result_read +/// @see opendal_code +/// @return Returns opendal_code +/// +/// \note If the read operation succeeds, the returned opendal_bytes is newly allocated on heap. +/// After your usage of that, please call opendal_bytes_free() to free the space. +/// +/// # Example +/// +/// Following is an example +/// ```C +/// // ... you have created an operator named op +/// +/// opendal_result_operator_reader result = opendal_operator_reader(op, "/testpath"); +/// assert(result.error == NULL); +/// // The reader is in result.reader +/// opendal_reader *reader = result.reader; +/// ``` +/// +/// # 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_reader( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_operator_reader { + 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() }; + match op.reader(path) { + Ok(reader) => opendal_result_operator_reader { + reader: Box::into_raw(Box::new(opendal_reader::new(reader))), + error: std::ptr::null_mut(), + }, + Err(e) => opendal_result_operator_reader { + reader: std::ptr::null_mut(), + error: opendal_error::new(e), + }, + } +} + +/// \brief Blockingly delete the object in `path`. +/// +/// Delete the object in `path` blockingly by `op_ptr`. +/// Error is NULL if successful, otherwise it contains the error code and error message. +/// +/// @param ptr The opendal_operator created previously +/// @param path The designated path you want to delete +/// @see opendal_operator +/// @see opendal_error +/// @return NULL if succeeds, otherwise it contains the error code and error message. +/// +/// # Example +/// +/// Following is an example +/// ```C +/// //...prepare your opendal_operator, named ptr for example +/// +/// // prepare your data +/// char* data = "Hello, World!"; +/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; +/// opendal_error *error = opendal_operator_write(ptr, "/testpath", bytes); +/// +/// assert(error == NULL); +/// +/// // now you can delete! +/// opendal_error *error = opendal_operator_delete(ptr, "/testpath"); +/// +/// // Assert that this succeeds +/// assert(error == NULL); +/// ``` +/// +/// # 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_delete( + op: *const opendal_operator, + path: *const c_char, +) -> *mut opendal_error { + 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() }; + match op.delete(path) { + Ok(_) => std::ptr::null_mut(), + Err(e) => opendal_error::new(e), + } +} + +/// \brief Check whether the path exists. +/// +/// If the operation succeeds, no matter the path exists or not, +/// the error should be a nullptr. Otherwise, the field `is_exist` +/// is filled with false, and the error is set +/// +/// @param ptr The opendal_operator created previously +/// @param path The path you want to check existence +/// @see opendal_operator +/// @see opendal_result_is_exist +/// @see opendal_error +/// @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. +/// However, it the operation fails, the `is_exist` will contains false and the error will be set. +/// +/// # Example +/// +/// ```C +/// // .. you previously wrote some data to path "/mytest/obj" +/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/mytest/obj"); +/// assert(e.error == NULL); +/// assert(e.is_exist); +/// +/// // but you previously did **not** write any data to path "/yourtest/obj" +/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/yourtest/obj"); +/// assert(e.error == NULL); +/// assert(!e.is_exist); +/// ``` +/// +/// # 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_is_exist( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_is_exist { + 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() }; + match op.is_exist(path) { + Ok(e) => opendal_result_is_exist { + is_exist: e, + error: std::ptr::null_mut(), + }, + Err(e) => opendal_result_is_exist { + is_exist: false, + error: opendal_error::new(e), + }, + } +} + +/// \brief Stat the path, return its metadata. +/// +/// Error is NULL if successful, otherwise it contains the error code and error message. +/// +/// @param ptr The opendal_operator created previously +/// @param path The path you want to stat +/// @see opendal_operator +/// @see opendal_result_stat +/// @see opendal_metadata +/// @return Returns opendal_result_stat, containing a metadata and an opendal_error. +/// If the operation succeeds, the `meta` field would holds a valid metadata and +/// the `error` field should hold nullptr. Otherwise the metadata will contain a +/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. +/// +/// # Example +/// +/// ```C +/// // ... previously you wrote "Hello, World!" to path "/testpath" +/// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); +/// assert(s.error == NULL); +/// +/// const opendal_metadata *meta = s.meta; +/// +/// // ... you could now use your metadata, notice that please only access metadata +/// // using the APIs provided by OpenDAL +/// ``` +/// +/// # 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_stat( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_stat { + 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() }; + match op.stat(path) { + Ok(m) => opendal_result_stat { + meta: Box::into_raw(Box::new(opendal_metadata::new(m))), + error: std::ptr::null_mut(), + }, + Err(e) => opendal_result_stat { + meta: std::ptr::null_mut(), + error: opendal_error::new(e), + }, + } +} + +/// \brief Blockingly list the objects in `path`. +/// +/// List the object in `path` blockingly by `op_ptr`, return a result with a +/// opendal_lister. Users should call opendal_lister_next() on the +/// lister. +/// +/// @param ptr The opendal_operator created previously +/// @param path The designated path you want to delete +/// @see opendal_lister +/// @return Returns opendal_result_list, containing a lister and an opendal_error. +/// If the operation succeeds, the `lister` field would holds a valid lister and +/// the `error` field should hold nullptr. Otherwise the `lister`` will contain a +/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. +/// +/// # Example +/// +/// Following is an example +/// ```C +/// // You have written some data into some files path "root/dir1" +/// // Your opendal_operator was called ptr +/// opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); +/// assert(l.error == ERROR); +/// +/// opendal_lister *lister = l.lister; +/// opendal_list_entry *entry; +/// +/// while ((entry = opendal_lister_next(lister)) != NULL) { +/// const char* de_path = opendal_list_entry_path(entry); +/// const char* de_name = opendal_list_entry_name(entry); +/// // ...... your operations +/// +/// // remember to free the entry after you are done using it +/// opendal_list_entry_free(entry); +/// } +/// +/// // and remember to free the lister +/// opendal_lister_free(lister); +/// ``` +/// +/// # 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_list( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_list { + 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() }; + match op.lister(path) { + Ok(lister) => opendal_result_list { + lister: Box::into_raw(Box::new(opendal_lister::new(lister))), + error: std::ptr::null_mut(), + }, + Err(e) => opendal_result_list { + lister: std::ptr::null_mut(), + error: opendal_error::new(e), + }, + } +} diff --git a/bindings/c/src/reader.rs b/bindings/c/src/reader.rs new file mode 100644 index 000000000000..f9c52fc17f73 --- /dev/null +++ b/bindings/c/src/reader.rs @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use super::*; +use ::opendal as core; +use std::io::Read; + +/// \brief The result type returned by opendal's reader operation. +/// +/// \note The opendal_reader actually owns a pointer to +/// a opendal::BlockingReader, which is inside the Rust core code. +#[repr(C)] +pub struct opendal_reader { + inner: *mut core::BlockingReader, +} + +impl opendal_reader { + pub(crate) fn new(reader: core::BlockingReader) -> Self { + Self { + inner: Box::into_raw(Box::new(reader)), + } + } + + /// \brief Read data from the reader. + #[no_mangle] + pub unsafe extern "C" fn opendal_reader_read( + reader: *const Self, + buf: *mut u8, + len: usize, + ) -> opendal_result_reader_read { + if buf.is_null() { + panic!("The buffer given is pointing at NULL"); + } + + let buf = unsafe { std::slice::from_raw_parts_mut(buf, len) }; + + let inner = unsafe { &mut *(*reader).inner }; + let r = inner.read(buf); + match r { + Ok(n) => opendal_result_reader_read { + size: n, + error: std::ptr::null_mut(), + }, + Err(e) => opendal_result_reader_read { + size: 0, + error: opendal_error::new( + core::Error::new(core::ErrorKind::Unexpected, "read failed from reader") + .set_source(e), + ), + }, + } + } + + /// \brief Frees the heap memory used by the opendal_reader. + #[no_mangle] + pub unsafe extern "C" fn opendal_reader_free(ptr: *mut opendal_reader) { + if !ptr.is_null() { + let _ = unsafe { Box::from_raw((*ptr).inner) }; + let _ = unsafe { Box::from_raw(ptr) }; + } + } +} diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs index 610e6d43e5ea..64b27892288f 100644 --- a/bindings/c/src/result.rs +++ b/bindings/c/src/result.rs @@ -17,29 +17,26 @@ //! This is for better naming in C header file. If we use generics for Result type, //! it will no doubt work find. However, the generics will lead to naming like -//! "opendal_result_opendal_operator_ptr", which is unacceptable. Therefore, +//! "opendal_result_opendal_operator", which is unacceptable. Therefore, //! we are defining all Result types here -use crate::error::opendal_error; -use crate::types::opendal_blocking_lister; -use crate::types::opendal_bytes; -use crate::types::opendal_metadata; -use crate::types::opendal_operator_ptr; -use crate::types::opendal_reader; +use super::*; /// \brief The result type returned by opendal_operator_new() operation. /// -/// If the init logic is successful, the `operator_ptr` field will be set to a valid +/// If the init logic is successful, the `op` field will be set to a valid /// pointer, and the `error` field will be set to null. If the init logic fails, the -/// `operator_ptr` field will be set to null, and the `error` field will be set to a +/// `op` field will be set to null, and the `error` field will be set to a /// valid pointer with error code and error message. /// /// @see opendal_operator_new() -/// @see opendal_operator_ptr +/// @see opendal_operator /// @see opendal_error #[repr(C)] pub struct opendal_result_operator_new { - pub operator_ptr: *mut opendal_operator_ptr, + /// The pointer for operator. + pub op: *mut opendal_operator, + /// The error pointer for error. pub error: *mut opendal_error, } @@ -86,15 +83,28 @@ pub struct opendal_result_stat { pub error: *mut opendal_error, } -/// \brief The result type returned by opendal_operator_blocking_list(). +/// \brief The result type returned by opendal_operator_list(). /// -/// The result type for opendal_operator_blocking_list(), the field `lister` contains the lister +/// The result type for opendal_operator_list(), the field `lister` contains the lister /// of the path, which is an iterator of the objects under the path. the field `error` represents /// whether the stat operation is successful. If successful, the `error` field is null. #[repr(C)] pub struct opendal_result_list { /// The lister, used for further listing operations - pub lister: *mut opendal_blocking_lister, + pub lister: *mut opendal_lister, + /// The error, if ok, it is null + pub error: *mut opendal_error, +} + +/// \brief The result type returned by opendal_lister_next(). +/// The list entry is the list result of the list operation, the error field is the error code and error message. +/// If the operation succeeds, the error should be NULL. +/// +/// \note Please notice if the lister reaches the end, both the list_entry and error will be NULL. +#[repr(C)] +pub struct opendal_result_lister_next { + /// The next object name + pub entry: *mut opendal_entry, /// The error, if ok, it is null pub error: *mut opendal_error, } @@ -104,7 +114,20 @@ pub struct opendal_result_list { /// of the path, which is an iterator of the objects under the path. the field `code` represents /// whether the stat operation is successful. #[repr(C)] -pub struct opendal_result_reader { +pub struct opendal_result_operator_reader { + /// The pointer for opendal_reader pub reader: *mut opendal_reader, + /// The error, if ok, it is null + pub error: *mut opendal_error, +} + +/// \brief The is the result type returned by opendal_reader_read(). +/// The result type contains a size field, which is the size of the data read, +/// which is zero on error. The error field is the error code and error message. +#[repr(C)] +pub struct opendal_result_reader_read { + /// The read size if succeed. + pub size: usize, + /// The error, if ok, it is null pub error: *mut opendal_error, } diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index e58b2e842128..3e8b88c48caf 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -16,77 +16,8 @@ // under the License. use std::collections::HashMap; -use std::ffi::CString; -use std::io::Read; use std::os::raw::c_char; -use ::opendal as od; - -use crate::error::opendal_code; -use crate::error::opendal_error; - -/// \brief Used to access almost all OpenDAL APIs. It represents a -/// operator that provides the unified interfaces provided by OpenDAL. -/// -/// @see opendal_operator_new This function construct the operator -/// @see opendal_operator_free This function frees the heap memory of the operator -/// -/// \note The opendal_operator_ptr actually owns a pointer to -/// a opendal::BlockingOperator, which is inside the Rust core code. -/// -/// \remark You may use the field `ptr` to check whether this is a NULL -/// operator. -#[repr(C)] -pub struct opendal_operator_ptr { - /// The pointer to the opendal::BlockingOperator in the Rust code. - /// Only touch this on judging whether it is NULL. - ptr: *const od::BlockingOperator, -} - -impl opendal_operator_ptr { - /// \brief Free the heap-allocated operator pointed by opendal_operator_ptr. - /// - /// Please only use this for a pointer pointing at a valid opendal_operator_ptr. - /// Calling this function on NULL does nothing, but calling this function on pointers - /// of other type will lead to segfault. - /// - /// # Example - /// - /// ```C - /// opendal_operator_ptr *ptr = opendal_operator_new("fs", NULL); - /// // ... use this ptr, maybe some reads and writes - /// - /// // free this operator - /// opendal_operator_free(ptr); - /// ``` - #[no_mangle] - pub unsafe extern "C" fn opendal_operator_free(op: *const opendal_operator_ptr) { - let _ = unsafe { Box::from_raw((*op).ptr as *mut od::BlockingOperator) }; - let _ = unsafe { Box::from_raw(op as *mut opendal_operator_ptr) }; - } -} - -impl opendal_operator_ptr { - /// Returns a reference to the underlying [`od::BlockingOperator`] - pub(crate) fn as_ref(&self) -> &od::BlockingOperator { - unsafe { &*(self.ptr) } - } -} - -#[allow(clippy::from_over_into)] -impl From<*const od::BlockingOperator> for opendal_operator_ptr { - fn from(value: *const od::BlockingOperator) -> Self { - Self { ptr: value } - } -} - -#[allow(clippy::from_over_into)] -impl From<*mut od::BlockingOperator> for opendal_operator_ptr { - fn from(value: *mut od::BlockingOperator) -> Self { - Self { ptr: value } - } -} - /// \brief opendal_bytes carries raw-bytes with its length /// /// The opendal_bytes type is a C-compatible substitute for Vec type @@ -113,13 +44,13 @@ impl opendal_bytes { /// \brief Frees the heap memory used by the opendal_bytes #[no_mangle] - pub extern "C" fn opendal_bytes_free(ptr: *mut opendal_bytes) { - if !ptr.is_null() { - let data_mut = unsafe { (*ptr).data as *mut u8 }; + pub unsafe extern "C" fn opendal_bytes_free(bs: *mut opendal_bytes) { + if !bs.is_null() { + let data_mut = unsafe { (*bs).data as *mut u8 }; // free the vector - let _ = unsafe { Vec::from_raw_parts(data_mut, (*ptr).len, (*ptr).len) }; + let _ = unsafe { Vec::from_raw_parts(data_mut, (*bs).len, (*bs).len) }; // free the pointer - let _ = unsafe { Box::from_raw(ptr) }; + let _ = unsafe { Box::from_raw(bs) }; } } } @@ -132,103 +63,7 @@ impl Into for opendal_bytes { } } -/// \brief Carries all metadata associated with a path. -/// -/// The metadata of the "thing" under a path. Please **only** use the opendal_metadata -/// with our provided API, e.g. opendal_metadata_content_length(). -/// -/// \note The metadata is also heap-allocated, please call opendal_metadata_free() on this -/// to free the heap memory. -/// -/// @see opendal_metadata_free -#[repr(C)] -pub struct opendal_metadata { - /// The pointer to the opendal::Metadata in the Rust code. - /// Only touch this on judging whether it is NULL. - pub inner: *mut od::Metadata, -} - -impl opendal_metadata { - /// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible - /// [`opendal_metadata`] - pub(crate) fn new(m: od::Metadata) -> Self { - Self { - inner: Box::into_raw(Box::new(m)), - } - } - - /// \brief Free the heap-allocated metadata used by opendal_metadata - #[no_mangle] - pub extern "C" fn opendal_metadata_free(ptr: *mut opendal_metadata) { - unsafe { - let _ = Box::from_raw((*ptr).inner); - let _ = Box::from_raw(ptr); - } - } - - /// \brief Return the content_length of the metadata - /// - /// # Example - /// ```C - /// // ... previously you wrote "Hello, World!" to path "/testpath" - /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - /// assert(s.error == NULL); - /// - /// opendal_metadata *meta = s.meta; - /// assert(opendal_metadata_content_length(meta) == 13); - /// ``` - #[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() } - } - - /// \brief Return whether the path represents a file - /// - /// # Example - /// ```C - /// // ... previously you wrote "Hello, World!" to path "/testpath" - /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - /// assert(s.error == NULL); - /// - /// opendal_metadata *meta = s.meta; - /// assert(opendal_metadata_is_file(meta)); - /// ``` - #[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 - let m = unsafe { &*self.inner }; - - m.is_file() - } - - /// \brief Return whether the path represents a directory - /// - /// # Example - /// ```C - /// // ... previously you wrote "Hello, World!" to path "/testpath" - /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - /// assert(s.error == NULL); - /// - /// opendal_metadata *meta = s.meta; - /// - /// // this is not a directory - /// assert(!opendal_metadata_is_dir(meta)); - /// ``` - /// - /// \todo This is not a very clear example. A clearer example will be added - /// after we support opendal_operator_mkdir() - #[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() } - } -} - -/// \brief The configuration for the initialization of opendal_operator_ptr. +/// \brief The configuration for the initialization of opendal_operator. /// /// \note This is also a heap-allocated struct, please free it after you use it /// @@ -308,164 +143,3 @@ impl opendal_operator_options { let _ = unsafe { Box::from_raw(options as *mut opendal_operator_options) }; } } - -/// \brief BlockingLister is designed to list entries at given path in a blocking -/// manner. -/// -/// Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) -/// -/// For examples, please see the comment section of opendal_operator_blocking_list() -/// @see opendal_operator_blocking_list() -#[repr(C)] -pub struct opendal_blocking_lister { - inner: *mut od::BlockingLister, -} - -impl opendal_blocking_lister { - pub(crate) fn new(lister: od::BlockingLister) -> Self { - Self { - inner: Box::into_raw(Box::new(lister)), - } - } - - /// \brief Return the next object to be listed - /// - /// Lister is an iterator of the objects under its path, this method is the same as - /// calling next() on the iterator - /// - /// For examples, please see the comment section of opendal_operator_blocking_list() - /// @see opendal_operator_blocking_list() - #[no_mangle] - pub unsafe extern "C" fn opendal_lister_next(&self) -> *mut opendal_list_entry { - let e = (*self.inner).next(); - if e.is_none() { - return std::ptr::null_mut(); - } - - match e.unwrap() { - Ok(e) => Box::into_raw(Box::new(opendal_list_entry::new(e))), - Err(_) => std::ptr::null_mut(), - } - } - - /// \brief Free the heap-allocated metadata used by opendal_blocking_lister - #[no_mangle] - pub unsafe extern "C" fn opendal_lister_free(p: *const opendal_blocking_lister) { - unsafe { - let _ = Box::from_raw((*p).inner); - let _ = Box::from_raw(p as *mut opendal_blocking_lister); - } - } -} - -/// \brief opendal_list_entry is the entry under a path, which is listed from the opendal_blocking_lister -/// -/// For examples, please see the comment section of opendal_operator_blocking_list() -/// @see opendal_operator_blocking_list() -/// @see opendal_list_entry_path() -/// @see opendal_list_entry_name() -#[repr(C)] -pub struct opendal_list_entry { - inner: *mut od::Entry, -} - -impl opendal_list_entry { - /// Used to convert the Rust type into C type - pub(crate) fn new(entry: od::Entry) -> Self { - Self { - inner: Box::into_raw(Box::new(entry)), - } - } - - /// \brief Path of entry. - /// - /// Path is relative to operator's root. Only valid in current operator. - /// - /// \note To free the string, you can directly call free() - #[no_mangle] - pub unsafe extern "C" fn opendal_list_entry_path(&self) -> *mut c_char { - let s = (*self.inner).path(); - let c_str = CString::new(s).unwrap(); - c_str.into_raw() - } - - /// \brief Name of entry. - /// - /// Name is the last segment of path. - /// If this entry is a dir, `Name` MUST endswith `/` - /// Otherwise, `Name` MUST NOT endswith `/`. - /// - /// \note To free the string, you can directly call free() - #[no_mangle] - pub unsafe extern "C" fn opendal_list_entry_name(&self) -> *mut c_char { - let s = (*self.inner).name(); - let c_str = CString::new(s).unwrap(); - c_str.into_raw() - } - - /// \brief Frees the heap memory used by the opendal_list_entry - #[no_mangle] - pub unsafe extern "C" fn opendal_list_entry_free(ptr: *mut opendal_list_entry) { - if !ptr.is_null() { - let _ = unsafe { Box::from_raw((*ptr).inner) }; - let _ = unsafe { Box::from_raw(ptr) }; - } - } -} - -#[repr(C)] -pub struct opendal_reader { - inner: *mut od::BlockingReader, -} - -#[repr(C)] -pub struct opendal_result_reader_read { - pub size: usize, - pub error: *mut opendal_error, -} - -impl opendal_reader { - pub(crate) fn new(reader: od::BlockingReader) -> Self { - Self { - inner: Box::into_raw(Box::new(reader)), - } - } - - #[no_mangle] - pub unsafe extern "C" fn opendal_reader_read( - &self, - buf: *mut u8, - len: usize, - ) -> opendal_result_reader_read { - if buf.is_null() { - panic!("The buffer given is pointing at NULL"); - } - let buf = unsafe { std::slice::from_raw_parts_mut(buf, len) }; - let r = (*self.inner).read_exact(buf); - - match r { - Ok(_) => opendal_result_reader_read { - size: len, - error: std::ptr::null_mut(), - }, - Err(e) => { - let e = Box::new(opendal_error::manual_error( - opendal_code::OPENDAL_UNEXPECTED, - e.to_string(), - )); - opendal_result_reader_read { - size: 0, - error: Box::into_raw(e), - } - } - } - } - - #[no_mangle] - pub unsafe extern "C" fn opendal_reader_free(ptr: *mut opendal_reader) { - 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 e5a94d2afd2b..fd33a0125527 100644 --- a/bindings/c/tests/bdd.cpp +++ b/bindings/c/tests/bdd.cpp @@ -25,7 +25,7 @@ extern "C" { class OpendalBddTest : public ::testing::Test { protected: - const opendal_operator_ptr* p; + const opendal_operator* p; std::string scheme; std::string path; @@ -44,7 +44,7 @@ class OpendalBddTest : public ::testing::Test { opendal_result_operator_new result = opendal_operator_new(scheme.c_str(), options); EXPECT_TRUE(result.error == nullptr); - this->p = result.operator_ptr; + this->p = result.op; EXPECT_TRUE(this->p); opendal_operator_options_free(options); @@ -61,7 +61,7 @@ TEST_F(OpendalBddTest, FeatureTest) .data = (uint8_t*)this->content.c_str(), .len = this->content.length(), }; - opendal_error* error = opendal_operator_blocking_write(this->p, this->path.c_str(), data); + opendal_error* error = opendal_operator_write(this->p, this->path.c_str(), data); EXPECT_EQ(error, nullptr); // The blocking file "test" should exist @@ -80,7 +80,7 @@ TEST_F(OpendalBddTest, FeatureTest) opendal_metadata_free(meta); // The blocking file "test" must have content "Hello, World!" - struct opendal_result_read r = opendal_operator_blocking_read(this->p, this->path.c_str()); + struct opendal_result_read r = opendal_operator_read(this->p, this->path.c_str()); EXPECT_EQ(r.error, nullptr); EXPECT_EQ(r.data->len, this->content.length()); for (int i = 0; i < r.data->len; i++) { @@ -90,7 +90,7 @@ TEST_F(OpendalBddTest, FeatureTest) // The blocking file "test" must have content "Hello, World!" and read into buffer int length = this->content.length(); unsigned char buffer[this->content.length()]; - opendal_result_reader reader = opendal_operator_blocking_reader(this->p, this->path.c_str()); + opendal_result_operator_reader reader = opendal_operator_reader(this->p, this->path.c_str()); EXPECT_EQ(reader.error, nullptr); auto rst = opendal_reader_read(reader.reader, buffer, length); EXPECT_EQ(rst.size, length); @@ -100,14 +100,14 @@ TEST_F(OpendalBddTest, FeatureTest) opendal_reader_free(reader.reader); // The blocking file should be deleted - error = opendal_operator_blocking_delete(this->p, this->path.c_str()); + 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_blocking_delete(this->p, this->path.c_str()); + error = opendal_operator_delete(this->p, this->path.c_str()); EXPECT_EQ(error, nullptr); opendal_bytes_free(r.data); diff --git a/bindings/c/tests/error_msg.cpp b/bindings/c/tests/error_msg.cpp index 3d06f4532dc5..f2087ea3b0bf 100644 --- a/bindings/c/tests/error_msg.cpp +++ b/bindings/c/tests/error_msg.cpp @@ -24,7 +24,7 @@ extern "C" { class OpendalErrorTest : public ::testing::Test { protected: - const opendal_operator_ptr* p; + const opendal_operator* p; // set up a brand new operator void SetUp() override @@ -35,7 +35,7 @@ class OpendalErrorTest : public ::testing::Test { opendal_result_operator_new result = opendal_operator_new("memory", options); EXPECT_TRUE(result.error == nullptr); - this->p = result.operator_ptr; + this->p = result.op; EXPECT_TRUE(this->p); opendal_operator_options_free(options); @@ -49,7 +49,7 @@ TEST_F(OpendalErrorTest, ErrorReadTest) { // Initialize a operator for "memory" backend, with no options // The read is supposed to fail - opendal_result_read r = opendal_operator_blocking_read(this->p, "/testpath"); + opendal_result_read r = opendal_operator_read(this->p, "/testpath"); ASSERT_NE(r.error, nullptr); ASSERT_EQ(r.error->code, OPENDAL_NOT_FOUND); diff --git a/bindings/c/tests/list.cpp b/bindings/c/tests/list.cpp index 7b003e6a61fb..410823973751 100644 --- a/bindings/c/tests/list.cpp +++ b/bindings/c/tests/list.cpp @@ -27,7 +27,7 @@ extern "C" { class OpendalListTest : public ::testing::Test { protected: - const opendal_operator_ptr* p; + const opendal_operator* p; // set up a brand new operator void SetUp() override @@ -38,7 +38,7 @@ class OpendalListTest : public ::testing::Test { opendal_result_operator_new result = opendal_operator_new("memory", options); EXPECT_TRUE(result.error == nullptr); - this->p = result.operator_ptr; + this->p = result.op; EXPECT_TRUE(this->p); opendal_operator_options_free(options); @@ -65,21 +65,23 @@ TEST_F(OpendalListTest, ListDirTest) }; // write must succeed - EXPECT_EQ(opendal_operator_blocking_write(this->p, path.c_str(), data), + EXPECT_EQ(opendal_operator_write(this->p, path.c_str(), data), nullptr); // list must succeed since the write succeeded - opendal_result_list l = opendal_operator_blocking_list(this->p, (dname + "/").c_str()); + opendal_result_list l = opendal_operator_list(this->p, (dname + "/").c_str()); EXPECT_EQ(l.error, nullptr); - opendal_blocking_lister* lister = l.lister; + opendal_lister* lister = l.lister; // start checking the lister's result bool found = false; - opendal_list_entry* entry = opendal_lister_next(lister); + opendal_result_lister_next result = opendal_lister_next(lister); + EXPECT_EQ(result.error, nullptr); + opendal_entry* entry = result.entry; while (entry) { - char* de_path = opendal_list_entry_path(entry); + char* de_path = opendal_entry_path(entry); // stat must succeed opendal_result_stat s = opendal_operator_stat(this->p, de_path); @@ -95,16 +97,18 @@ TEST_F(OpendalListTest, ListDirTest) free(de_path); opendal_metadata_free(s.meta); - opendal_list_entry_free(entry); + opendal_entry_free(entry); - entry = opendal_lister_next(lister); + result = opendal_lister_next(lister); + EXPECT_EQ(result.error, nullptr); + entry = result.entry; } // we must have found the file we wrote EXPECT_TRUE(found); // delete - EXPECT_EQ(opendal_operator_blocking_delete(this->p, path.c_str()), + EXPECT_EQ(opendal_operator_delete(this->p, path.c_str()), nullptr); opendal_lister_free(lister); diff --git a/bindings/c/upgrade.md b/bindings/c/upgrade.md new file mode 100644 index 000000000000..db84e0d5ea95 --- /dev/null +++ b/bindings/c/upgrade.md @@ -0,0 +1,18 @@ +# Upgrade to v0.42 + +The naming convention for C binding has been altered. + +## Struct Naming + +Renaming certain struct names for consistency. + +- `opendal_operator_ptr` => `opendal_operator` +- `opendal_blocking_lister` => `opendal_lister` +- `opendal_list_entry` => `opendal_entry` + +## API Naming + +We've eliminated the `blocking_` prefix from our API because C binding doesn't currently support async. In the future, we plan to introduce APIs such as `opendal_operator_async_write`. + +- `opendal_operator_blocking_write` => `opendal_operator_write` +- `opendal_operator_blocking_read` => `opendal_operator_read` diff --git a/bindings/go/opendal.go b/bindings/go/opendal.go index 4677a99dc675..027d87ad24e7 100644 --- a/bindings/go/opendal.go +++ b/bindings/go/opendal.go @@ -37,7 +37,7 @@ var ( type Options map[string]string type Operator struct { - inner *C.opendal_operator_ptr + inner *C.opendal_operator } func NewOperator(scheme string, opt Options) (*Operator, error) { @@ -56,7 +56,7 @@ func NewOperator(scheme string, opt Options) (*Operator, error) { return nil, errors.New(fmt.Sprintf("create operator failed, error code: %d, error message: %s", code, message)) } return &Operator{ - inner: ret.operator_ptr, + inner: ret.op, }, nil } @@ -65,7 +65,7 @@ func (o *Operator) Write(key string, value []byte) error { return errValueEmpty } bytes := C.opendal_bytes{data: (*C.uchar)(unsafe.Pointer(&value[0])), len: C.ulong(len(value))} - ret := C.opendal_operator_blocking_write(o.inner, C.CString(key), bytes) + ret := C.opendal_operator_write(o.inner, C.CString(key), bytes) if ret != nil { defer C.opendal_error_free(ret) code, message := parseError(ret) @@ -75,7 +75,7 @@ func (o *Operator) Write(key string, value []byte) error { } func (o *Operator) Read(key string) ([]byte, error) { - ret := C.opendal_operator_blocking_read(o.inner, C.CString(key)) + ret := C.opendal_operator_read(o.inner, C.CString(key)) if ret.error != nil { defer C.opendal_error_free(ret.error) code, message := parseError(ret.error) diff --git a/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift b/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift index 6104c17c15d5..2ce27551e39e 100644 --- a/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift +++ b/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift @@ -24,7 +24,7 @@ public struct OperatorError: Error { } public class Operator { - var nativeOp: UnsafePointer + var nativeOp: UnsafePointer deinit { opendal_operator_free(nativeOp) @@ -54,14 +54,14 @@ public class Operator { ) } - self.nativeOp = UnsafePointer(ret.operator_ptr)! + self.nativeOp = UnsafePointer(ret.op)! } public func blockingWrite(_ data: Data, to path: String) throws { let ret = data.withUnsafeBytes { dataPointer in let address = dataPointer.baseAddress!.assumingMemoryBound(to: UInt8.self) let bytes = opendal_bytes(data: address, len: UInt(dataPointer.count)) - return opendal_operator_blocking_write(nativeOp, path, bytes) + return opendal_operator_write(nativeOp, path, bytes) } if let err = ret { @@ -79,7 +79,7 @@ public class Operator { } public func blockingRead(_ path: String) throws -> Data { - let ret = opendal_operator_blocking_read(nativeOp, path) + let ret = opendal_operator_read(nativeOp, path) if let err = ret.error { defer { opendal_error_free(err) diff --git a/bindings/zig/test/bdd.zig b/bindings/zig/test/bdd.zig index c835e45b3400..68548d1f9fd9 100644 --- a/bindings/zig/test/bdd.zig +++ b/bindings/zig/test/bdd.zig @@ -24,7 +24,7 @@ test "Opendal BDD test" { const c_str = [*:0]const u8; // define a type for 'const char*' in C const OpendalBDDTest = struct { - p: [*c]const opendal.c.opendal_operator_ptr, + p: [*c]const opendal.c.opendal_operator, scheme: c_str, path: c_str, content: c_str, @@ -42,7 +42,7 @@ test "Opendal BDD test" { // Given A new OpenDAL Blocking Operator var result = opendal.c.opendal_operator_new(self.scheme, options); testing.expectEqual(result.@"error", null) catch unreachable; - self.p = result.operator_ptr; + self.p = result.op; return self; } @@ -63,7 +63,7 @@ test "Opendal BDD test" { // c_str does not have len field (.* is ptr) .len = std.mem.len(testkit.content), }; - const result = opendal.c.opendal_operator_blocking_write(testkit.p, testkit.path, data); + const result = opendal.c.opendal_operator_write(testkit.p, testkit.path, data); try testing.expectEqual(result, null); // The blocking file "test" should exist @@ -82,7 +82,7 @@ test "Opendal BDD test" { defer opendal.c.opendal_metadata_free(meta); // The blocking file "test" must have content "Hello, World!" - var r: opendal.c.opendal_result_read = opendal.c.opendal_operator_blocking_read(testkit.p, testkit.path); + var r: opendal.c.opendal_result_read = opendal.c.opendal_operator_read(testkit.p, testkit.path); defer opendal.c.opendal_bytes_free(r.data); try testing.expect(r.@"error" == null); try testing.expectEqual(std.mem.len(testkit.content), r.data.*.len);