This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 9515e8b36 refactor(bindings/C): Implement error with error message
(#3250)
9515e8b36 is described below
commit 9515e8b3648a2634f59feb1b0c1cdda6c9de1676
Author: Xinyou Ji <[email protected]>
AuthorDate: Fri Oct 13 08:14:51 2023 -0400
refactor(bindings/C): Implement error with error message (#3250)
* Implement opendal_error
* docs
* tests
* error msg example
* free
* separate the valgrind test w/ original test
* docs
* add opendal_operator_new result
* refactor the test to use new operator init logic
* refactor the examples to use the new operator_ptr init logic
* add .gitignore
* add error_msg tests to valgrind
* rename basicrw.c to basic.c
* resolve comments
* fix fmt
* Fix zig
Signed-off-by: Xuanwo <[email protected]>
* Fix go
Signed-off-by: Xuanwo <[email protected]>
* Fix swift
Signed-off-by: Xuanwo <[email protected]>
* Format code
Signed-off-by: Xuanwo <[email protected]>
---------
Signed-off-by: Xuanwo <[email protected]>
Co-authored-by: Xuanwo <[email protected]>
---
.github/workflows/bindings_c.yml | 6 +-
bindings/c/.gitignore | 7 +
bindings/c/Makefile | 8 +
bindings/c/README.md | 6 +-
bindings/c/examples/.gitignore | 1 -
bindings/c/examples/{basicrw.c => basic.c} | 13 +-
bindings/c/examples/{basicrw.c => error_handle.c} | 35 ++-
bindings/c/include/opendal.h | 240 ++++++++++++---------
bindings/c/src/error.rs | 83 +++++--
bindings/c/src/lib.rs | 202 ++++++++++-------
bindings/c/src/result.rs | 50 +++--
bindings/c/src/types.rs | 10 +-
bindings/c/tests/bdd.cpp | 27 +--
bindings/c/tests/error_msg.cpp | 72 +++++++
bindings/c/tests/list.cpp | 15 +-
bindings/go/opendal.go | 37 +++-
bindings/go/opendal_test.go | 4 +-
bindings/swift/.swift-format | 11 +
bindings/swift/OpenDAL/Package.swift | 11 +-
.../OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift | 20 +-
.../swift/OpenDAL/Sources/OpenDAL/Operator.swift | 95 ++++----
.../OpenDAL/Tests/OpenDALTests/OpenDALTests.swift | 23 +-
bindings/zig/README.md | 2 +-
bindings/zig/src/opendal.zig | 3 -
bindings/zig/test/bdd.zig | 14 +-
25 files changed, 638 insertions(+), 357 deletions(-)
diff --git a/.github/workflows/bindings_c.yml b/.github/workflows/bindings_c.yml
index 7bcac37cc..dccfcda03 100644
--- a/.github/workflows/bindings_c.yml
+++ b/.github/workflows/bindings_c.yml
@@ -64,6 +64,10 @@ jobs:
- name: Check diff
run: git diff --exit-code
- - name: Build and Run BDD tests
+ - name: Build and Run tests
working-directory: "bindings/c"
run: make test
+
+ - name: Build and Run memory-leak tests
+ working-directory: "bindings/c"
+ run: make memory_leak
diff --git a/bindings/c/.gitignore b/bindings/c/.gitignore
index 8f5beed0e..355533f65 100644
--- a/bindings/c/.gitignore
+++ b/bindings/c/.gitignore
@@ -2,3 +2,10 @@ build/
docs/
*.o
+
+examples/*
+!examples/
+!examples/*.h
+!examples/*.c
+!examples/*.cc
+!examples/*.cpp
\ No newline at end of file
diff --git a/bindings/c/Makefile b/bindings/c/Makefile
index 0337f4d90..e10f47bdf 100644
--- a/bindings/c/Makefile
+++ b/bindings/c/Makefile
@@ -44,8 +44,16 @@ build:
test:
$(CXX) tests/bdd.cpp -o $(OBJ_DIR)/bdd $(CXXFLAGS) $(LDFLAGS) $(LIBS)
$(CXX) tests/list.cpp -o $(OBJ_DIR)/list $(CXXFLAGS) $(LDFLAGS) $(LIBS)
+ $(CXX) tests/error_msg.cpp -o $(OBJ_DIR)/error_msg $(CXXFLAGS)
$(LDFLAGS) $(LIBS)
+ $(OBJ_DIR)/bdd
+ $(OBJ_DIR)/list
+ $(OBJ_DIR)/error_msg
+
+.PHONY: test_memory_leak
+memory_leak:
$(VALGRIND) $(OBJ_DIR)/bdd
$(VALGRIND) $(OBJ_DIR)/list
+ $(VALGRIND) $(OBJ_DIR)/error_msg
.PHONY: doc
doc:
diff --git a/bindings/c/README.md b/bindings/c/README.md
index 7d57c27fc..f89cead26 100644
--- a/bindings/c/README.md
+++ b/bindings/c/README.md
@@ -24,13 +24,13 @@ int main()
};
/* Write this into path "/testpath" */
- opendal_code code = opendal_operator_blocking_write(op, "/testpath", data);
- assert(code == OPENDAL_OK);
+ opendal_error *error = opendal_operator_blocking_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_bytes* read_bytes = r.data;
- assert(r.code == OPENDAL_OK);
+ assert(r.error == NULL);
assert(read_bytes->len == 24);
/* Lets print it out */
diff --git a/bindings/c/examples/.gitignore b/bindings/c/examples/.gitignore
deleted file mode 100644
index 887b0251a..000000000
--- a/bindings/c/examples/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-basicrw
diff --git a/bindings/c/examples/basicrw.c b/bindings/c/examples/basic.c
similarity index 83%
copy from bindings/c/examples/basicrw.c
copy to bindings/c/examples/basic.c
index 3c3cd75cd..88071847c 100644
--- a/bindings/c/examples/basicrw.c
+++ b/bindings/c/examples/basic.c
@@ -24,8 +24,11 @@
int main()
{
/* Initialize a operator for "memory" backend, with no options */
- const 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);
+
+ opendal_operator_ptr* op = result.operator_ptr;
/* Prepare some data to be written */
opendal_bytes data = {
@@ -34,13 +37,13 @@ int main()
};
/* Write this into path "/testpath" */
- opendal_code code = opendal_operator_blocking_write(op, "/testpath", data);
- assert(code == OPENDAL_OK);
+ opendal_error* error = opendal_operator_blocking_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_bytes* read_bytes = r.data;
- assert(r.code == OPENDAL_OK);
+ assert(r.error == NULL);
assert(read_bytes->len == 24);
/* Lets print it out */
diff --git a/bindings/c/examples/basicrw.c b/bindings/c/examples/error_handle.c
similarity index 58%
rename from bindings/c/examples/basicrw.c
rename to bindings/c/examples/error_handle.c
index 3c3cd75cd..3bb3308e7 100644
--- a/bindings/c/examples/basicrw.c
+++ b/bindings/c/examples/error_handle.c
@@ -21,36 +21,29 @@
#include "opendal.h"
#include "stdio.h"
+// this example shows how to get error message from opendal_error
int main()
{
/* Initialize a operator for "memory" backend, with no options */
- const 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 = {
- .data = (uint8_t*)"this_string_length_is_24",
- .len = 24,
- };
+ opendal_operator_ptr* op = result.operator_ptr;
- /* Write this into path "/testpath" */
- opendal_code code = opendal_operator_blocking_write(op, "/testpath", data);
- assert(code == OPENDAL_OK);
-
- /* We can read it out, make sure the data is the same */
+ /* The read is supposed to fail */
opendal_result_read r = opendal_operator_blocking_read(op, "/testpath");
- opendal_bytes* read_bytes = r.data;
- assert(r.code == OPENDAL_OK);
- assert(read_bytes->len == 24);
+ assert(r.error != NULL);
+ assert(r.error->code == OPENDAL_NOT_FOUND);
- /* Lets print it out */
- for (int i = 0; i < 24; ++i) {
- printf("%c", read_bytes->data[i]);
+ /* Lets print the error message out */
+ struct opendal_bytes* error_msg = &r.error->message;
+ for (int i = 0; i < error_msg->len; ++i) {
+ printf("%c", error_msg->data[i]);
}
- printf("\n");
- /* the opendal_bytes read is heap allocated, please free it */
- opendal_bytes_free(read_bytes);
+ /* free the error since the error is not NULL */
+ opendal_error_free(r.error);
/* the operator_ptr is also heap allocated */
opendal_operator_free(op);
diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index 95c0be52d..51d66aadb 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -31,14 +31,6 @@
* added in the future.
*/
typedef enum opendal_code {
- /**
- * All is well
- */
- OPENDAL_OK,
- /**
- * General error
- */
- OPENDAL_ERROR,
/**
* returning it back. For example, s3 returns an internal service error.
*/
@@ -230,24 +222,6 @@ typedef struct opendal_operator_ptr {
const struct BlockingOperator *ptr;
} opendal_operator_ptr;
-/**
- * \brief The configuration for the initialization of opendal_operator_ptr.
- *
- * \note This is also a heap-allocated struct, please free it after you use it
- *
- * @see opendal_operator_new has an example of using opendal_operator_options
- * @see opendal_operator_options_new This function construct the operator
- * @see opendal_operator_options_free This function frees the heap memory of
the operator
- * @see opendal_operator_options_set This function allow you to set the options
- */
-typedef struct opendal_operator_options {
- /**
- * The pointer to the Rust HashMap<String, String>
- * Only touch this on judging whether it is NULL.
- */
- struct HashMap_String__String *inner;
-} opendal_operator_options;
-
/**
* \brief opendal_bytes carries raw-bytes with its length
*
@@ -268,13 +242,72 @@ typedef struct opendal_bytes {
uintptr_t len;
} opendal_bytes;
+/**
+ * \brief The opendal error type for C binding, containing an error code and
corresponding error
+ * message.
+ *
+ * The normal operations returns a pointer to the opendal_error, and the
**nullptr normally
+ * represents no error has taken placed**. If any error has taken place, the
caller should check
+ * the error code and print the error message.
+ *
+ * The error code is represented in opendal_code, which is a enum on different
type of errors.
+ * The error messages is represented in opendal_bytes, which is a non-null
terminated byte array.
+ *
+ * \note 1. The error message is on heap, so the error needs to be freed by
the caller, by calling
+ * opendal_error_free. 2. The error message is not null terminated, so
the caller should
+ * never use "%s" to print the error message.
+ *
+ * @see opendal_code
+ * @see opendal_bytes
+ * @see opendal_error_free
+ */
+typedef struct opendal_error {
+ enum opendal_code code;
+ struct opendal_bytes message;
+} opendal_error;
+
+/**
+ * \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
+ * 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
+ * valid pointer with error code and error message.
+ *
+ * @see opendal_operator_new()
+ * @see opendal_operator_ptr
+ * @see opendal_error
+ */
+typedef struct opendal_result_operator_new {
+ struct opendal_operator_ptr *operator_ptr;
+ struct opendal_error *error;
+} opendal_result_operator_new;
+
+/**
+ * \brief The configuration for the initialization of opendal_operator_ptr.
+ *
+ * \note This is also a heap-allocated struct, please free it after you use it
+ *
+ * @see opendal_operator_new has an example of using opendal_operator_options
+ * @see opendal_operator_options_new This function construct the operator
+ * @see opendal_operator_options_free This function frees the heap memory of
the operator
+ * @see opendal_operator_options_set This function allow you to set the options
+ */
+typedef struct opendal_operator_options {
+ /**
+ * The pointer to the Rust HashMap<String, String>
+ * Only touch this on judging whether it is NULL.
+ */
+ struct HashMap_String__String *inner;
+} opendal_operator_options;
+
/**
* \brief The result type returned by opendal's read operation.
*
* The result type of read operation in opendal C binding, it contains
- * the data that the read operation returns and a error code.
+ * the data that the read operation returns and an NULL error.
* If the read operation failed, the `data` fields should be a nullptr
- * and the error code is **NOT** OPENDAL_OK.
+ * and the error is not NULL.
*/
typedef struct opendal_result_read {
/**
@@ -282,17 +315,17 @@ typedef struct opendal_result_read {
*/
struct opendal_bytes *data;
/**
- * The error code, should be OPENDAL_OK if succeeds
+ * The error, if ok, it is null
*/
- enum opendal_code code;
+ struct opendal_error *error;
} opendal_result_read;
/**
* \brief The result type returned by opendal_operator_is_exist().
*
* The result type for opendal_operator_is_exist(), the field `is_exist`
- * contains whether the path exists, and the field `code` contains the
- * corresponding error code.
+ * contains whether the path exists, and the field `error` contains the
+ * corresponding error. If successful, the `error` field is null.
*
* \note If the opendal_operator_is_exist() fails, the `is_exist` field
* will be set to false.
@@ -303,9 +336,9 @@ typedef struct opendal_result_is_exist {
*/
bool is_exist;
/**
- * The error code, should be OPENDAL_OK if succeeds
+ * The error, if ok, it is null
*/
- enum opendal_code code;
+ struct opendal_error *error;
} opendal_result_is_exist;
/**
@@ -331,7 +364,8 @@ typedef struct opendal_metadata {
* \brief The result type returned by opendal_operator_stat().
*
* The result type for opendal_operator_stat(), the field `meta` contains the
metadata
- * of the path, the field `code` represents whether the stat operation is
successful.
+ * of the path, the field `error` represents whether the stat operation is
successful.
+ * If successful, the `error` field is null.
*/
typedef struct opendal_result_stat {
/**
@@ -339,9 +373,9 @@ typedef struct opendal_result_stat {
*/
struct opendal_metadata *meta;
/**
- * The error code, should be OPENDAL_OK if succeeds
+ * The error, if ok, it is null
*/
- enum opendal_code code;
+ struct opendal_error *error;
} opendal_result_stat;
/**
@@ -361,12 +395,18 @@ typedef struct opendal_blocking_lister {
* \brief The result type returned by opendal_operator_blocking_list().
*
* The result type for opendal_operator_blocking_list(), the field `lister`
contains the lister
- * of the path, which is an iterator of the objects under the path. the field
`code` represents
- * whether the stat operation is successful.
+ * 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.
*/
typedef struct opendal_result_list {
+ /**
+ * The lister, used for further listing operations
+ */
struct opendal_blocking_lister *lister;
- enum opendal_code code;
+ /**
+ * The error, if ok, it is null
+ */
+ struct opendal_error *error;
} opendal_result_list;
/**
@@ -397,10 +437,9 @@ extern "C" {
* @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_operator_ptr setup with the `scheme` and `options`
is the construction
- * succeeds. A null opendal_operator_ptr if any error happens.
- *
- * \remark You may use the `ptr` field of opendal_operator_ptr to check if it
is NULL.
+ * @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
*
@@ -412,7 +451,8 @@ extern "C" {
* opendal_operator_options_set(options, "root", "/myroot");
*
* // Construct the operator based on the options and scheme
- * const opendal_operator_ptr *ptr = opendal_operator_new("memory", options);
+ * 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);
@@ -422,30 +462,27 @@ extern "C" {
*
* # Safety
*
- * It is **safe** under two cases below
- * * The memory pointed to by `scheme` contain a valid nul terminator at the
end of
- * the string.
- * * The `scheme` points to NULL, this function simply returns you a null
opendal_operator_ptr.
+ * The only unsafe case is passing a invalid c string pointer to the `scheme`
argument.
*/
-const struct opendal_operator_ptr *opendal_operator_new(const char *scheme,
+struct opendal_result_operator_new opendal_operator_new(const char *scheme,
const struct
opendal_operator_options *options);
/**
* \brief Blockingly write raw bytes to `path`.
*
- * Write the `bytes` into the `path` blockingly by `op_ptr`, returns the
opendal_code OPENDAL_OK
- * if succeeds, others otherwise.
+ * 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.
+ * \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_code
- * @return OPENDAL_OK if succeeds others otherwise
+ * @see opendal_error
+ * @return NULL if succeeds, otherwise it contains the error code and error
message.
*
* # Example
*
@@ -458,10 +495,10 @@ const struct opendal_operator_ptr
*opendal_operator_new(const char *scheme,
* opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 };
*
* // now you can write!
- * opendal_code code = opendal_operator_blocking_write(ptr, "/testpath",
bytes);
+ * opendal_error *err = opendal_operator_blocking_write(ptr, "/testpath",
bytes);
*
* // Assert that this succeeds
- * assert(code == OPENDAL_OK)
+ * assert(err == NULL);
* ```
*
* # Safety
@@ -476,24 +513,23 @@ const struct opendal_operator_ptr
*opendal_operator_new(const char *scheme,
*
* * If the `path` points to NULL, this function panics, i.e. exits with
information
*/
-enum opendal_code opendal_operator_blocking_write(const struct
opendal_operator_ptr *ptr,
- const char *path,
- struct opendal_bytes bytes);
+struct opendal_error *opendal_operator_blocking_write(const struct
opendal_operator_ptr *ptr,
+ const char *path,
+ struct opendal_bytes
bytes);
/**
* \brief Blockingly read the data from `path`.
*
- * Read the data out from `path` blockingly by operator, returns
- * an opendal_result_read with error code.
+ * 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_code
+ * @see opendal_error
* @return Returns opendal_result_read, the `data` field is a pointer to a
newly allocated
- * opendal_bytes, the `code` field contains the error code. If the `code` is
not OPENDAL_OK,
- * the `data` field points to NULL.
+ * 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.
@@ -505,7 +541,7 @@ enum opendal_code opendal_operator_blocking_write(const
struct opendal_operator_
* // ... you have write "Hello, World!" to path "/testpath"
*
* opendal_result_read r = opendal_operator_blocking_read(ptr, "testpath");
- * assert(r.code == OPENDAL_OK);
+ * assert(r.error == NULL);
*
* opendal_bytes *bytes = r.data;
* assert(bytes->len == 13);
@@ -527,14 +563,14 @@ struct opendal_result_read
opendal_operator_blocking_read(const struct opendal_o
/**
* \brief Blockingly delete the object in `path`.
*
- * Delete the object in `path` blockingly by `op_ptr`, returns the
opendal_code OPENDAL_OK
- * if succeeds, others otherwise
+ * 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_code
- * @return OPENDAL_OK if succeeds others otherwise
+ * @see opendal_error
+ * @return NULL if succeeds, otherwise it contains the error code and error
message.
*
* # Example
*
@@ -545,13 +581,15 @@ struct opendal_result_read
opendal_operator_blocking_read(const struct opendal_o
* // prepare your data
* char* data = "Hello, World!";
* opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 };
- * opendal_code code = opendal_operator_blocking_write(ptr, "/testpath",
bytes);
+ * opendal_error *error = opendal_operator_blocking_write(ptr, "/testpath",
bytes);
+ *
+ * assert(error == NULL);
*
* // now you can delete!
- * opendal_code code = opendal_operator_blocking_delete(ptr, "/testpath");
+ * opendal_error *error = opendal_operator_blocking_delete(ptr, "/testpath");
*
* // Assert that this succeeds
- * assert(code == OPENDAL_OK)
+ * assert(error == NULL);
* ```
*
* # Safety
@@ -564,37 +602,35 @@ 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
*/
-enum opendal_code opendal_operator_blocking_delete(const struct
opendal_operator_ptr *ptr,
- const char *path);
+struct opendal_error *opendal_operator_blocking_delete(const struct
opendal_operator_ptr *ptr,
+ const char *path);
/**
* \brief Check whether the path exists.
*
* If the operation succeeds, no matter the path exists or not,
- * the error code should be opendal_code::OPENDAL_OK. Otherwise,
- * the field `is_exist` is filled with false, and the error code
- * is set correspondingly.
+ * 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_code
+ * @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 code will be
- * stored in the `code` field.
+ * 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.code == OPENDAL_OK);
+ * 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.code == OPENDAL_OK);
+ * opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/yourtest/obj");
+ * assert(e.error == NULL);
* assert(!e.is_exist);
* ```
*
@@ -614,26 +650,24 @@ struct opendal_result_is_exist
opendal_operator_is_exist(const struct opendal_op
/**
* \brief Stat the path, return its metadata.
*
- * If the operation succeeds, the error code should be
- * OPENDAL_OK. Otherwise, the field `meta` is filled with
- * a NULL pointer, and the error code is set correspondingly.
+ * 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 a
opendal_code.
+ * @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 `code` field should hold OPENDAL_OK. Otherwise the metadata will
contain a
- * NULL pointer, i.e. invalid, and the `code` will be set correspondingly.
+ * 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.code == OPENDAL_OK);
+ * assert(s.error == NULL);
*
* const opendal_metadata *meta = s.meta;
*
@@ -664,7 +698,10 @@ struct opendal_result_stat opendal_operator_stat(const
struct opendal_operator_p
* @param ptr The opendal_operator_ptr created previously
* @param path The designated path you want to delete
* @see opendal_blocking_lister
- * @return
+ * @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
*
@@ -673,7 +710,7 @@ struct opendal_result_stat opendal_operator_stat(const
struct opendal_operator_p
* // 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.code == OPENDAL_OK);
+ * assert(l.error == ERROR);
*
* opendal_blocking_lister *lister = l.lister;
* opendal_list_entry *entry;
@@ -704,6 +741,11 @@ struct opendal_result_stat opendal_operator_stat(const
struct opendal_operator_p
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.
*
@@ -740,7 +782,7 @@ void opendal_metadata_free(struct opendal_metadata *ptr);
* ```C
* // ... previously you wrote "Hello, World!" to path "/testpath"
* opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- * assert(s.code == OPENDAL_OK);
+ * assert(s.error == NULL);
*
* opendal_metadata *meta = s.meta;
* assert(opendal_metadata_content_length(meta) == 13);
@@ -755,7 +797,7 @@ uint64_t opendal_metadata_content_length(const struct
opendal_metadata *self);
* ```C
* // ... previously you wrote "Hello, World!" to path "/testpath"
* opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- * assert(s.code == OPENDAL_OK);
+ * assert(s.error == NULL);
*
* opendal_metadata *meta = s.meta;
* assert(opendal_metadata_is_file(meta));
@@ -770,7 +812,7 @@ bool opendal_metadata_is_file(const struct opendal_metadata
*self);
* ```C
* // ... previously you wrote "Hello, World!" to path "/testpath"
* opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- * assert(s.code == OPENDAL_OK);
+ * assert(s.error == NULL);
*
* opendal_metadata *meta = s.meta;
*
@@ -842,7 +884,7 @@ void opendal_lister_free(const struct
opendal_blocking_lister *p);
*
* Path is relative to operator's root. Only valid in current operator.
*
- * @NOTE To free the string, you can directly call free()
+ * \note To free the string, you can directly call free()
*/
char *opendal_list_entry_path(const struct opendal_list_entry *self);
@@ -853,7 +895,7 @@ char *opendal_list_entry_path(const struct
opendal_list_entry *self);
* If this entry is a dir, `Name` MUST endswith `/`
* Otherwise, `Name` MUST NOT endswith `/`.
*
- * @NOTE To free the string, you can directly call free()
+ * \note To free the string, you can directly call free()
*/
char *opendal_list_entry_name(const struct opendal_list_entry *self);
diff --git a/bindings/c/src/error.rs b/bindings/c/src/error.rs
index 8aa8c87ca..366d646ae 100644
--- a/bindings/c/src/error.rs
+++ b/bindings/c/src/error.rs
@@ -17,20 +17,19 @@
use ::opendal as od;
-/// The wrapper type for opendal's error, wrapped because of the
-/// orphan rule
-struct opendal_error(od::Error);
+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 enum opendal_code {
- /// All is well
- OPENDAL_OK,
- /// General error
- // \todo: make details in the `opendal_error *`
- OPENDAL_ERROR,
+pub(crate) enum opendal_code {
/// returning it back. For example, s3 returns an internal service error.
OPENDAL_UNEXPECTED,
/// Underlying service doesn't support this operation.
@@ -53,14 +52,7 @@ pub enum opendal_code {
OPENDAL_IS_SAME_FILE,
}
-impl opendal_code {
- pub(crate) fn from_opendal_error(e: od::Error) -> Self {
- let error = opendal_error(e);
- error.error_code()
- }
-}
-
-impl opendal_error {
+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;
@@ -81,3 +73,60 @@ impl opendal_error {
}
}
}
+
+/// \brief The opendal error type for C binding, containing an error code and
corresponding error
+/// message.
+///
+/// The normal operations returns a pointer to the opendal_error, and the
**nullptr normally
+/// represents no error has taken placed**. If any error has taken place, the
caller should check
+/// the error code and print the error message.
+///
+/// The error code is represented in opendal_code, which is a enum on
different type of errors.
+/// The error messages is represented in opendal_bytes, which is a non-null
terminated byte array.
+///
+/// \note 1. The error message is on heap, so the error needs to be freed by
the caller, by calling
+/// opendal_error_free. 2. The error message is not null terminated, so
the caller should
+/// never use "%s" to print the error message.
+///
+/// @see opendal_code
+/// @see opendal_bytes
+/// @see opendal_error_free
+#[repr(C)]
+pub struct opendal_error {
+ code: opendal_code,
+ message: opendal_bytes,
+}
+
+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 }
+ }
+
+ pub(crate) fn manual_error(code: opendal_code, message: String) -> Self {
+ let message = opendal_bytes::new(message.into_bytes());
+ opendal_error { code, message }
+ }
+
+ /// \brief Frees the opendal_error, ok to call on NULL
+ #[no_mangle]
+ pub unsafe extern "C" fn opendal_error_free(ptr: *mut opendal_error) {
+ if !ptr.is_null() {
+ let message_ptr = &(*ptr).message as *const opendal_bytes as *mut
opendal_bytes;
+ if !message_ptr.is_null() {
+ let data_mut = unsafe { (*message_ptr).data as *mut u8 };
+ let _ = unsafe {
+ Vec::from_raw_parts(data_mut, (*message_ptr).len,
(*message_ptr).len)
+ };
+ }
+
+ // free the pointer
+ let _ = unsafe { Box::from_raw(ptr) };
+ }
+ }
+}
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs
index 6535b7965..013e2a307 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/lib.rs
@@ -34,10 +34,11 @@ use std::os::raw::c_char;
use std::str::FromStr;
use ::opendal as od;
+use error::opendal_error;
use result::opendal_result_list;
+use result::opendal_result_operator_new;
use types::opendal_blocking_lister;
-use crate::error::opendal_code;
use crate::result::opendal_result_is_exist;
use crate::result::opendal_result_read;
use crate::result::opendal_result_stat;
@@ -57,10 +58,9 @@ use crate::types::opendal_operator_ptr;
/// @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_operator_ptr setup with the `scheme` and `options`
is the construction
-/// succeeds. A null opendal_operator_ptr if any error happens.
-///
-/// \remark You may use the `ptr` field of opendal_operator_ptr to check if it
is NULL.
+/// @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
///
@@ -72,7 +72,8 @@ use crate::types::opendal_operator_ptr;
/// opendal_operator_options_set(options, "root", "/myroot");
///
/// // Construct the operator based on the options and scheme
-/// const opendal_operator_ptr *ptr = opendal_operator_new("memory", options);
+/// 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);
@@ -82,24 +83,34 @@ use crate::types::opendal_operator_ptr;
///
/// # Safety
///
-/// It is **safe** under two cases below
-/// * The memory pointed to by `scheme` contain a valid nul terminator at the
end of
-/// the string.
-/// * The `scheme` points to NULL, this function simply returns you a null
opendal_operator_ptr.
+/// 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,
-) -> *const opendal_operator_ptr {
+) -> opendal_result_operator_new {
if scheme.is_null() {
- return std::ptr::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(_) => {
- return std::ptr::null();
+ 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;
}
};
@@ -112,32 +123,39 @@ pub unsafe extern "C" fn opendal_operator_new(
let op = match od::Operator::via_map(scheme, map) {
Ok(o) => o.blocking(),
- Err(_) => {
- return std::ptr::null();
+ 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;
}
};
// this prevents the operator memory from being dropped by the Box
let op = opendal_operator_ptr::from(Box::into_raw(Box::new(op)));
-
- Box::into_raw(Box::new(op))
+ opendal_result_operator_new {
+ operator_ptr: Box::into_raw(Box::new(op)),
+ error: std::ptr::null_mut(),
+ }
}
/// \brief Blockingly write raw bytes to `path`.
///
-/// Write the `bytes` into the `path` blockingly by `op_ptr`, returns the
opendal_code OPENDAL_OK
-/// if succeeds, others otherwise.
+/// 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.
+/// \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_code
-/// @return OPENDAL_OK if succeeds others otherwise
+/// @see opendal_error
+/// @return NULL if succeeds, otherwise it contains the error code and error
message.
///
/// # Example
///
@@ -150,10 +168,10 @@ pub unsafe extern "C" fn opendal_operator_new(
/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 };
///
/// // now you can write!
-/// opendal_code code = opendal_operator_blocking_write(ptr, "/testpath",
bytes);
+/// opendal_error *err = opendal_operator_blocking_write(ptr, "/testpath",
bytes);
///
/// // Assert that this succeeds
-/// assert(code == OPENDAL_OK)
+/// assert(err == NULL);
/// ```
///
/// # Safety
@@ -172,7 +190,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
ptr: *const opendal_operator_ptr,
path: *const c_char,
bytes: opendal_bytes,
-) -> opendal_code {
+) -> *mut opendal_error {
if path.is_null() {
panic!("The path given is pointing at NULL");
}
@@ -180,24 +198,26 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
let op = (*ptr).as_ref();
let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
match op.write(path, bytes) {
- Ok(_) => opendal_code::OPENDAL_OK,
- Err(e) => opendal_code::from_opendal_error(e),
+ Ok(_) => std::ptr::null_mut(),
+ Err(e) => {
+ let e = Box::new(opendal_error::from_opendal_error(e));
+ 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.
+/// 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_code
+/// @see opendal_error
/// @return Returns opendal_result_read, the `data` field is a pointer to a
newly allocated
-/// opendal_bytes, the `code` field contains the error code. If the `code` is
not OPENDAL_OK,
-/// the `data` field points to NULL.
+/// 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.
@@ -209,7 +229,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
/// // ... you have write "Hello, World!" to path "/testpath"
///
/// opendal_result_read r = opendal_operator_blocking_read(ptr, "testpath");
-/// assert(r.code == OPENDAL_OK);
+/// assert(r.error == NULL);
///
/// opendal_bytes *bytes = r.data;
/// assert(bytes->len == 13);
@@ -241,26 +261,29 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
let v = Box::new(opendal_bytes::new(d));
opendal_result_read {
data: Box::into_raw(v),
- code: opendal_code::OPENDAL_OK,
+ 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),
}
}
- Err(e) => opendal_result_read {
- data: std::ptr::null_mut(),
- code: opendal_code::from_opendal_error(e),
- },
}
}
/// \brief Blockingly delete the object in `path`.
///
-/// Delete the object in `path` blockingly by `op_ptr`, returns the
opendal_code OPENDAL_OK
-/// if succeeds, others otherwise
+/// 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_code
-/// @return OPENDAL_OK if succeeds others otherwise
+/// @see opendal_error
+/// @return NULL if succeeds, otherwise it contains the error code and error
message.
///
/// # Example
///
@@ -271,13 +294,15 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
/// // prepare your data
/// char* data = "Hello, World!";
/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 };
-/// opendal_code code = opendal_operator_blocking_write(ptr, "/testpath",
bytes);
+/// opendal_error *error = opendal_operator_blocking_write(ptr, "/testpath",
bytes);
+///
+/// assert(error == NULL);
///
/// // now you can delete!
-/// opendal_code code = opendal_operator_blocking_delete(ptr, "/testpath");
+/// opendal_error *error = opendal_operator_blocking_delete(ptr, "/testpath");
///
/// // Assert that this succeeds
-/// assert(code == OPENDAL_OK)
+/// assert(error == NULL);
/// ```
///
/// # Safety
@@ -293,7 +318,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
pub unsafe extern "C" fn opendal_operator_blocking_delete(
ptr: *const opendal_operator_ptr,
path: *const c_char,
-) -> opendal_code {
+) -> *mut opendal_error {
if path.is_null() {
panic!("The path given is pointing at NULL");
}
@@ -301,38 +326,39 @@ pub unsafe extern "C" fn opendal_operator_blocking_delete(
let op = (*ptr).as_ref();
let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
match op.delete(path) {
- Ok(_) => opendal_code::OPENDAL_OK,
- Err(e) => opendal_code::from_opendal_error(e),
+ 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 code should be opendal_code::OPENDAL_OK. Otherwise,
-/// the field `is_exist` is filled with false, and the error code
-/// is set correspondingly.
+/// 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_code
+/// @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 code will be
-/// stored in the `code` field.
+/// 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.code == OPENDAL_OK);
+/// 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.code == OPENDAL_OK);
+/// opendal_result_is_exist e = opendal_operator_is_exist(ptr,
"/yourtest/obj");
+/// assert(e.error == NULL);
/// assert(!e.is_exist);
/// ```
///
@@ -359,37 +385,38 @@ pub unsafe extern "C" fn opendal_operator_is_exist(
match op.is_exist(path) {
Ok(e) => opendal_result_is_exist {
is_exist: e,
- code: opendal_code::OPENDAL_OK,
- },
- Err(err) => opendal_result_is_exist {
- is_exist: false,
- code: opendal_code::from_opendal_error(err),
+ 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.
///
-/// If the operation succeeds, the error code should be
-/// OPENDAL_OK. Otherwise, the field `meta` is filled with
-/// a NULL pointer, and the error code is set correspondingly.
+/// 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 a
opendal_code.
+/// @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 `code` field should hold OPENDAL_OK. Otherwise the metadata will
contain a
-/// NULL pointer, i.e. invalid, and the `code` will be set correspondingly.
+/// 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.code == OPENDAL_OK);
+/// assert(s.error == NULL);
///
/// const opendal_metadata *meta = s.meta;
///
@@ -420,12 +447,15 @@ pub unsafe extern "C" fn opendal_operator_stat(
match op.stat(path) {
Ok(m) => opendal_result_stat {
meta: Box::into_raw(Box::new(opendal_metadata::new(m))),
- code: opendal_code::OPENDAL_OK,
- },
- Err(err) => opendal_result_stat {
- meta: std::ptr::null_mut(),
- code: opendal_code::from_opendal_error(err),
+ 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),
+ }
+ }
}
}
@@ -438,7 +468,10 @@ pub unsafe extern "C" fn opendal_operator_stat(
/// @param ptr The opendal_operator_ptr created previously
/// @param path The designated path you want to delete
/// @see opendal_blocking_lister
-/// @return
+/// @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
///
@@ -447,7 +480,7 @@ pub unsafe extern "C" fn opendal_operator_stat(
/// // 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.code == OPENDAL_OK);
+/// assert(l.error == ERROR);
///
/// opendal_blocking_lister *lister = l.lister;
/// opendal_list_entry *entry;
@@ -488,12 +521,15 @@ pub unsafe extern "C" fn opendal_operator_blocking_list(
match op.lister(path) {
Ok(lister) => opendal_result_list {
lister:
Box::into_raw(Box::new(opendal_blocking_lister::new(lister))),
- code: opendal_code::OPENDAL_OK,
+ error: std::ptr::null_mut(),
},
- Err(e) => opendal_result_list {
- lister: std::ptr::null_mut(),
- code: opendal_code::from_opendal_error(e),
- },
+ 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),
+ }
+ }
}
}
diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs
index c0d113718..aa95c1190 100644
--- a/bindings/c/src/result.rs
+++ b/bindings/c/src/result.rs
@@ -20,30 +20,47 @@
//! "opendal_result_opendal_operator_ptr", which is unacceptable. Therefore,
//! we are defining all Result types here
-use crate::error::opendal_code;
+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;
+
+/// \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
+/// 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
+/// valid pointer with error code and error message.
+///
+/// @see opendal_operator_new()
+/// @see opendal_operator_ptr
+/// @see opendal_error
+#[repr(C)]
+pub struct opendal_result_operator_new {
+ pub operator_ptr: *mut opendal_operator_ptr,
+ pub error: *mut opendal_error,
+}
/// \brief The result type returned by opendal's read operation.
///
/// The result type of read operation in opendal C binding, it contains
-/// the data that the read operation returns and a error code.
+/// the data that the read operation returns and an NULL error.
/// If the read operation failed, the `data` fields should be a nullptr
-/// and the error code is **NOT** OPENDAL_OK.
+/// and the error is not NULL.
#[repr(C)]
pub struct opendal_result_read {
/// The byte array with length returned by read operations
pub data: *mut opendal_bytes,
- /// The error code, should be OPENDAL_OK if succeeds
- pub code: opendal_code,
+ /// The error, if ok, it is null
+ pub error: *mut opendal_error,
}
/// \brief The result type returned by opendal_operator_is_exist().
///
/// The result type for opendal_operator_is_exist(), the field `is_exist`
-/// contains whether the path exists, and the field `code` contains the
-/// corresponding error code.
+/// contains whether the path exists, and the field `error` contains the
+/// corresponding error. If successful, the `error` field is null.
///
/// \note If the opendal_operator_is_exist() fails, the `is_exist` field
/// will be set to false.
@@ -51,29 +68,32 @@ pub struct opendal_result_read {
pub struct opendal_result_is_exist {
/// Whether the path exists
pub is_exist: bool,
- /// The error code, should be OPENDAL_OK if succeeds
- pub code: opendal_code,
+ /// The error, if ok, it is null
+ pub error: *mut opendal_error,
}
/// \brief The result type returned by opendal_operator_stat().
///
/// The result type for opendal_operator_stat(), the field `meta` contains the
metadata
-/// of the path, the field `code` represents whether the stat operation is
successful.
+/// of 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_stat {
/// The metadata output of the stat
pub meta: *mut opendal_metadata,
- /// The error code, should be OPENDAL_OK if succeeds
- pub code: opendal_code,
+ /// The error, if ok, it is null
+ pub error: *mut opendal_error,
}
/// \brief The result type returned by opendal_operator_blocking_list().
///
/// The result type for opendal_operator_blocking_list(), the field `lister`
contains the lister
-/// of the path, which is an iterator of the objects under the path. the field
`code` represents
-/// whether the stat operation is successful.
+/// 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 code: opendal_code,
+ /// 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 89d02826e..dbc0aa68e 100644
--- a/bindings/c/src/types.rs
+++ b/bindings/c/src/types.rs
@@ -168,7 +168,7 @@ impl opendal_metadata {
/// ```C
/// // ... previously you wrote "Hello, World!" to path "/testpath"
/// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- /// assert(s.code == OPENDAL_OK);
+ /// assert(s.error == NULL);
///
/// opendal_metadata *meta = s.meta;
/// assert(opendal_metadata_content_length(meta) == 13);
@@ -186,7 +186,7 @@ impl opendal_metadata {
/// ```C
/// // ... previously you wrote "Hello, World!" to path "/testpath"
/// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- /// assert(s.code == OPENDAL_OK);
+ /// assert(s.error == NULL);
///
/// opendal_metadata *meta = s.meta;
/// assert(opendal_metadata_is_file(meta));
@@ -206,7 +206,7 @@ impl opendal_metadata {
/// ```C
/// // ... previously you wrote "Hello, World!" to path "/testpath"
/// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath");
- /// assert(s.code == OPENDAL_OK);
+ /// assert(s.error == NULL);
///
/// opendal_metadata *meta = s.meta;
///
@@ -377,7 +377,7 @@ impl opendal_list_entry {
///
/// Path is relative to operator's root. Only valid in current operator.
///
- /// @NOTE To free the string, you can directly call free()
+ /// \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();
@@ -391,7 +391,7 @@ impl opendal_list_entry {
/// If this entry is a dir, `Name` MUST endswith `/`
/// Otherwise, `Name` MUST NOT endswith `/`.
///
- /// @NOTE To free the string, you can directly call free()
+ /// \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();
diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp
index c7d797172..76d5047fa 100644
--- a/bindings/c/tests/bdd.cpp
+++ b/bindings/c/tests/bdd.cpp
@@ -41,8 +41,11 @@ protected:
opendal_operator_options_set(options, "root", "/myroot");
// Given A new OpenDAL Blocking Operator
- this->p = opendal_operator_new(scheme.c_str(), options);
- EXPECT_TRUE(this->p->ptr);
+ opendal_result_operator_new result =
opendal_operator_new(scheme.c_str(), options);
+ EXPECT_TRUE(result.error == nullptr);
+
+ this->p = result.operator_ptr;
+ EXPECT_TRUE(this->p);
opendal_operator_options_free(options);
}
@@ -58,17 +61,17 @@ TEST_F(OpendalBddTest, FeatureTest)
.data = (uint8_t*)this->content.c_str(),
.len = this->content.length(),
};
- opendal_code code = opendal_operator_blocking_write(this->p,
this->path.c_str(), data);
- EXPECT_EQ(code, OPENDAL_OK);
+ opendal_error* error = opendal_operator_blocking_write(this->p,
this->path.c_str(), data);
+ EXPECT_EQ(error, nullptr);
// The blocking file "test" should exist
opendal_result_is_exist e = opendal_operator_is_exist(this->p,
this->path.c_str());
- EXPECT_EQ(e.code, OPENDAL_OK);
+ EXPECT_EQ(e.error, nullptr);
EXPECT_TRUE(e.is_exist);
// The blocking file "test" entry mode must be file
opendal_result_stat s = opendal_operator_stat(this->p, this->path.c_str());
- EXPECT_EQ(s.code, OPENDAL_OK);
+ EXPECT_EQ(s.error, nullptr);
opendal_metadata* meta = s.meta;
EXPECT_TRUE(opendal_metadata_is_file(meta));
@@ -78,22 +81,22 @@ TEST_F(OpendalBddTest, FeatureTest)
// The blocking file "test" must have content "Hello, World!"
struct opendal_result_read r = opendal_operator_blocking_read(this->p,
this->path.c_str());
- EXPECT_EQ(r.code, OPENDAL_OK);
+ EXPECT_EQ(r.error, nullptr);
EXPECT_EQ(r.data->len, this->content.length());
for (int i = 0; i < r.data->len; i++) {
EXPECT_EQ(this->content[i], (char)(r.data->data[i]));
}
// The blocking file should be deleted
- code = opendal_operator_blocking_delete(this->p, this->path.c_str());
- EXPECT_EQ(code, OPENDAL_OK);
+ error = opendal_operator_blocking_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.code, OPENDAL_OK);
+ EXPECT_EQ(e.error, nullptr);
EXPECT_FALSE(e.is_exist);
// The deletion operation should be idempotent
- code = opendal_operator_blocking_delete(this->p, this->path.c_str());
- EXPECT_EQ(code, OPENDAL_OK);
+ error = opendal_operator_blocking_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
new file mode 100644
index 000000000..3d06f4532
--- /dev/null
+++ b/bindings/c/tests/error_msg.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "gtest/gtest.h"
+extern "C" {
+#include "opendal.h"
+}
+
+class OpendalErrorTest : public ::testing::Test {
+protected:
+ const opendal_operator_ptr* p;
+
+ // set up a brand new operator
+ void SetUp() override
+ {
+ opendal_operator_options* options = opendal_operator_options_new();
+ opendal_operator_options_set(options, "root", "/myroot");
+
+ opendal_result_operator_new result = opendal_operator_new("memory",
options);
+ EXPECT_TRUE(result.error == nullptr);
+
+ this->p = result.operator_ptr;
+ EXPECT_TRUE(this->p);
+
+ opendal_operator_options_free(options);
+ }
+
+ void TearDown() override { opendal_operator_free(this->p); }
+};
+
+// Test no memory leak of error message
+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");
+ ASSERT_NE(r.error, nullptr);
+ ASSERT_EQ(r.error->code, OPENDAL_NOT_FOUND);
+
+ // Lets check the error message out
+ struct opendal_bytes* error_msg = &r.error->message;
+ ASSERT_NE(error_msg->data, nullptr);
+ ASSERT_GT(error_msg->len, 0);
+
+ // the opendal_bytes read is heap allocated, please free it
+ opendal_bytes_free(r.data);
+
+ // free the error
+ opendal_error_free(r.error);
+}
+
+int main(int argc, char** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/bindings/c/tests/list.cpp b/bindings/c/tests/list.cpp
index b6b44dd44..7b003e6a6 100644
--- a/bindings/c/tests/list.cpp
+++ b/bindings/c/tests/list.cpp
@@ -35,8 +35,11 @@ protected:
opendal_operator_options* options = opendal_operator_options_new();
opendal_operator_options_set(options, "root", "/myroot");
- this->p = opendal_operator_new("memory", options);
- EXPECT_TRUE(this->p->ptr);
+ opendal_result_operator_new result = opendal_operator_new("memory",
options);
+ EXPECT_TRUE(result.error == nullptr);
+
+ this->p = result.operator_ptr;
+ EXPECT_TRUE(this->p);
opendal_operator_options_free(options);
}
@@ -63,11 +66,11 @@ TEST_F(OpendalListTest, ListDirTest)
// write must succeed
EXPECT_EQ(opendal_operator_blocking_write(this->p, path.c_str(), data),
- OPENDAL_OK);
+ nullptr);
// list must succeed since the write succeeded
opendal_result_list l = opendal_operator_blocking_list(this->p, (dname +
"/").c_str());
- EXPECT_EQ(l.code, OPENDAL_OK);
+ EXPECT_EQ(l.error, nullptr);
opendal_blocking_lister* lister = l.lister;
@@ -80,7 +83,7 @@ TEST_F(OpendalListTest, ListDirTest)
// stat must succeed
opendal_result_stat s = opendal_operator_stat(this->p, de_path);
- EXPECT_EQ(s.code, OPENDAL_OK);
+ EXPECT_EQ(s.error, nullptr);
if (!strcmp(de_path, path.c_str())) {
found = true;
@@ -102,7 +105,7 @@ TEST_F(OpendalListTest, ListDirTest)
// delete
EXPECT_EQ(opendal_operator_blocking_delete(this->p, path.c_str()),
- OPENDAL_OK);
+ nullptr);
opendal_lister_free(lister);
}
diff --git a/bindings/go/opendal.go b/bindings/go/opendal.go
index 1494ab170..4677a99dc 100644
--- a/bindings/go/opendal.go
+++ b/bindings/go/opendal.go
@@ -49,9 +49,14 @@ func NewOperator(scheme string, opt Options) (*Operator,
error) {
for k, v := range opt {
C.opendal_operator_options_set(opts, C.CString(k), C.CString(v))
}
- op := C.opendal_operator_new(C.CString(scheme), opts)
+ ret := C.opendal_operator_new(C.CString(scheme), opts)
+ if ret.error != nil {
+ defer C.opendal_error_free(ret.error)
+ code, message := parseError(ret.error)
+ return nil, errors.New(fmt.Sprintf("create operator failed,
error code: %d, error message: %s", code, message))
+ }
return &Operator{
- inner: op,
+ inner: ret.operator_ptr,
}, nil
}
@@ -61,22 +66,36 @@ func (o *Operator) Write(key string, value []byte) error {
}
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)
- if ret != 0 {
- return errors.New(fmt.Sprintf("write failed, error code: %d",
ret))
+ if ret != nil {
+ defer C.opendal_error_free(ret)
+ code, message := parseError(ret)
+ return errors.New(fmt.Sprintf("write failed, error code: %d,
error message: %s", code, message))
}
return nil
}
func (o *Operator) Read(key string) ([]byte, error) {
- result := C.opendal_operator_blocking_read(o.inner, C.CString(key))
- ret := int(result.code)
- if ret != 0 {
- return nil, errors.New(fmt.Sprintf("write failed, error code:
%d", ret))
+ ret := C.opendal_operator_blocking_read(o.inner, C.CString(key))
+ if ret.error != nil {
+ defer C.opendal_error_free(ret.error)
+ code, message := parseError(ret.error)
+ return nil, errors.New(fmt.Sprintf("read failed, error code:
%d, error message: %s", code, message))
}
- return C.GoBytes(unsafe.Pointer(result.data.data),
C.int(result.data.len)), nil
+ return C.GoBytes(unsafe.Pointer(ret.data.data), C.int(ret.data.len)),
nil
}
func (o *Operator) Close() error {
C.opendal_operator_free(o.inner)
return nil
}
+
+func decodeBytes(bs C.opendal_bytes) []byte {
+ return C.GoBytes(unsafe.Pointer(bs.data), C.int(bs.len))
+}
+
+func parseError(err *C.opendal_error) (int, string) {
+ code := int(err.code)
+ message := string(decodeBytes(err.message))
+
+ return code, message
+}
diff --git a/bindings/go/opendal_test.go b/bindings/go/opendal_test.go
index 6723b67e2..9ac76c273 100644
--- a/bindings/go/opendal_test.go
+++ b/bindings/go/opendal_test.go
@@ -38,7 +38,7 @@ func TestOperations(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "Hello World from OpenDAL CGO!", string(value))
- println(string(value))
+ println(string(value))
}
func BenchmarkOperator_Write(b *testing.B) {
@@ -50,7 +50,7 @@ func BenchmarkOperator_Write(b *testing.B) {
bs := bytes.Repeat([]byte("a"), 1024*1024)
op.Write("test", bs)
- b.SetBytes(1024*1024)
+ b.SetBytes(1024 * 1024)
b.ResetTimer()
for i := 0; i < b.N; i++ {
op.Read("test")
diff --git a/bindings/swift/.swift-format b/bindings/swift/.swift-format
new file mode 100644
index 000000000..d5d1e0158
--- /dev/null
+++ b/bindings/swift/.swift-format
@@ -0,0 +1,11 @@
+{
+ "version": 1,
+ "lineLength": 100,
+ "indentation": {
+ "spaces": 4
+ },
+ "maximumBlankLines": 1,
+ "respectsExistingLineBreaks": true,
+ "lineBreakBeforeControlFlowKeywords": true,
+ "lineBreakBeforeEachArgument": true
+}
diff --git a/bindings/swift/OpenDAL/Package.swift
b/bindings/swift/OpenDAL/Package.swift
index d31439f32..f861d7697 100644
--- a/bindings/swift/OpenDAL/Package.swift
+++ b/bindings/swift/OpenDAL/Package.swift
@@ -27,7 +27,8 @@ let package = Package(
products: [
.library(
name: "OpenDAL",
- targets: ["OpenDAL"]),
+ targets: ["OpenDAL"]
+ )
],
targets: [
.systemLibrary(name: "COpenDAL"),
@@ -35,10 +36,12 @@ let package = Package(
name: "OpenDAL",
dependencies: ["COpenDAL"],
linkerSettings: [
- .unsafeFlags(["-L\(packageRoot)/Sources/COpenDAL/lib"]),
- ]),
+ .unsafeFlags(["-L\(packageRoot)/Sources/COpenDAL/lib"])
+ ]
+ ),
.testTarget(
name: "OpenDALTests",
- dependencies: ["OpenDAL"]),
+ dependencies: ["OpenDAL"]
+ ),
]
)
diff --git a/bindings/swift/OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift
b/bindings/swift/OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift
index 92d0c8270..8e6193375 100644
--- a/bindings/swift/OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift
+++ b/bindings/swift/OpenDAL/Sources/OpenDAL/Data+OpenDAL.swift
@@ -15,8 +15,8 @@
// specific language governing permissions and limitations
// under the License.
-import Foundation
import COpenDAL
+import Foundation
extension Data {
/// Creates a new data by managing `opendal_bytes` as its
@@ -25,15 +25,15 @@ extension Data {
/// This can be used to read data from Rust with zero-copying.
/// The underlying buffer will be freed when the data gets
/// deallocated.
- init?(openDALBytes: UnsafeMutablePointer<opendal_bytes>) {
- guard let address = UnsafeRawPointer(openDALBytes.pointee.data) else {
- return nil
- }
+ init(openDALBytes: UnsafeMutablePointer<opendal_bytes>) {
+ let address = UnsafeRawPointer(openDALBytes.pointee.data)!
let length = Int(openDALBytes.pointee.len)
- self.init(bytesNoCopy: .init(mutating: address),
- count: length,
- deallocator: .custom({ _, _ in
- opendal_bytes_free(openDALBytes)
- }))
+ self.init(
+ bytesNoCopy: .init(mutating: address),
+ count: length,
+ deallocator: .custom({ _, _ in
+ opendal_bytes_free(openDALBytes)
+ })
+ )
}
}
diff --git a/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift
b/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift
index 1f2f91d1a..6104c17c1 100644
--- a/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift
+++ b/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift
@@ -15,75 +15,84 @@
// specific language governing permissions and limitations
// under the License.
-import Foundation
import COpenDAL
+import Foundation
-public enum OperatorError: Error {
- case failedToBuild
- case operationError(UInt32)
+public struct OperatorError: Error {
+ let code: UInt32
+ let message: Data
}
-/// A type used to access almost all OpenDAL APIs.
public class Operator {
var nativeOp: UnsafePointer<opendal_operator_ptr>
-
+
deinit {
opendal_operator_free(nativeOp)
}
-
- /// Creates an operator with the given options.
- ///
- /// - Parameter options: The option map for creating the operator.
- /// - Throws: `OperatorError` value that indicates an error if failed.
- public init(scheme: String, options: [String : String] = [:]) throws {
+
+ public init(scheme: String, options: [String: String] = [:]) throws {
let nativeOptions = opendal_operator_options_new()
defer {
opendal_operator_options_free(nativeOptions)
}
-
+
for option in options {
opendal_operator_options_set(nativeOptions, option.key,
option.value)
}
-
- guard let nativeOp = opendal_operator_new(scheme, nativeOptions) else {
- throw OperatorError.failedToBuild
- }
-
- guard nativeOp.pointee.ptr != nil else {
- throw OperatorError.failedToBuild
+
+ let ret = opendal_operator_new(scheme, nativeOptions)
+ if let err = ret.error {
+ defer {
+ opendal_error_free(err)
+ }
+ let immutableErr = err.pointee
+ let messagePointer = withUnsafePointer(to: immutableErr.message) {
$0 }
+ let messageLength = Int(immutableErr.message.len)
+ throw OperatorError(
+ code: immutableErr.code.rawValue,
+ message: Data(bytes: messagePointer, count: messageLength)
+ )
}
- self.nativeOp = nativeOp
+ self.nativeOp = UnsafePointer(ret.operator_ptr)!
}
-
- /// Blockingly write the data to a given path.
- ///
- /// - Parameter data: The content to be written.
- /// - Parameter path: The destination path for writing the data.
- /// - Throws: `OperatorError` value that indicates an error if failed to
write.
+
public func blockingWrite(_ data: Data, to path: String) throws {
- let code = data.withUnsafeBytes { dataPointer in
+ 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)
}
-
- guard code == OPENDAL_OK else {
- throw OperatorError.operationError(code.rawValue)
+
+ if let err = ret {
+ defer {
+ opendal_error_free(err)
+ }
+ let immutableErr = err.pointee
+ let messagePointer = withUnsafePointer(to: immutableErr.message) {
$0 }
+ let messageLength = Int(immutableErr.message.len)
+ throw OperatorError(
+ code: immutableErr.code.rawValue,
+ message: Data(bytes: messagePointer, count: messageLength)
+ )
}
}
-
- /// Blockingly read the data from a given path.
- ///
- /// - Parameter path: Path of the data to read.
- /// - Returns: `NativeData` object if the data exists.
- /// - Throws: `OperatorError` value that indicates an error if failed to
read.
- public func blockingRead(_ path: String) throws -> Data? {
- let result = opendal_operator_blocking_read(nativeOp, path)
- guard result.code == OPENDAL_OK else {
- throw OperatorError.operationError(result.code.rawValue)
+
+ public func blockingRead(_ path: String) throws -> Data {
+ let ret = opendal_operator_blocking_read(nativeOp, path)
+ if let err = ret.error {
+ defer {
+ opendal_error_free(err)
+ }
+ let immutableErr = err.pointee
+ let messagePointer = withUnsafePointer(to: immutableErr.message) {
$0 }
+ let messageLength = Int(immutableErr.message.len)
+ throw OperatorError(
+ code: immutableErr.code.rawValue,
+ message: Data(bytes: messagePointer, count: messageLength)
+ )
}
-
- return .init(openDALBytes: result.data)
+
+ return Data(openDALBytes: ret.data)
}
}
diff --git a/bindings/swift/OpenDAL/Tests/OpenDALTests/OpenDALTests.swift
b/bindings/swift/OpenDAL/Tests/OpenDALTests/OpenDALTests.swift
index a776efdbd..ef17c7887 100644
--- a/bindings/swift/OpenDAL/Tests/OpenDALTests/OpenDALTests.swift
+++ b/bindings/swift/OpenDAL/Tests/OpenDALTests/OpenDALTests.swift
@@ -16,26 +16,27 @@
// under the License.
import XCTest
+
@testable import OpenDAL
final class OpenDALTests: XCTestCase {
func testSimple() throws {
- let op = try Operator(scheme: "memory", options: [
- "root": "/myroot"
- ])
-
+ let op = try Operator(
+ scheme: "memory",
+ options: [
+ "root": "/myroot"
+ ]
+ )
+
let testContents = Data([1, 2, 3, 4])
try op.blockingWrite(testContents, to: "test")
-
- guard let readContents = try op.blockingRead("test") else {
- XCTFail("Expected a `Data`")
- return
- }
-
+
+ let readContents = try op.blockingRead("test")
+
for (testByte, readByte) in zip(testContents, readContents) {
XCTAssertEqual(testByte, readByte)
}
-
+
XCTAssertThrowsError(try op.blockingRead("test_not_exists"))
}
}
diff --git a/bindings/zig/README.md b/bindings/zig/README.md
index 7f25eb470..315c686cb 100644
--- a/bindings/zig/README.md
+++ b/bindings/zig/README.md
@@ -16,7 +16,7 @@ To compile OpenDAL from source code, you need:
# build libopendal_c (underneath call make -C ../c)
zig build libopendal_c
# build and run unit tests
-zig build test -fsummary
+zig build test --summary all
```
## License
diff --git a/bindings/zig/src/opendal.zig b/bindings/zig/src/opendal.zig
index 93c504742..02bfc41e9 100644
--- a/bindings/zig/src/opendal.zig
+++ b/bindings/zig/src/opendal.zig
@@ -19,8 +19,6 @@ pub const c = @cImport(@cInclude("opendal.h"));
// Zig code get values C code
pub const Code = enum(c.opendal_code) {
- OK = c.OPENDAL_OK,
- ERROR = c.OPENDAL_ERROR,
UNEXPECTED = c.OPENDAL_UNEXPECTED,
UNSUPPORTED = c.OPENDAL_UNSUPPORTED,
CONFIG_INVALID = c.OPENDAL_CONFIG_INVALID,
@@ -58,7 +56,6 @@ pub fn codeToError(code: c.opendal_code)
OpendalError!c.opendal_code {
c.OPENDAL_ALREADY_EXISTS => error.AlreadyExists,
c.OPENDAL_RATE_LIMITED => error.RateLimited,
c.OPENDAL_IS_SAME_FILE => error.IsSameFile,
- else => c.OPENDAL_ERROR,
};
}
pub fn errorToCode(err: OpendalError) c_int {
diff --git a/bindings/zig/test/bdd.zig b/bindings/zig/test/bdd.zig
index 79748fa3b..c835e45b3 100644
--- a/bindings/zig/test/bdd.zig
+++ b/bindings/zig/test/bdd.zig
@@ -40,7 +40,9 @@ test "Opendal BDD test" {
opendal.c.opendal_operator_options_set(options, "root", "/myroot");
// Given A new OpenDAL Blocking Operator
- self.p = opendal.c.opendal_operator_new(self.scheme, options);
+ var result = opendal.c.opendal_operator_new(self.scheme, options);
+ testing.expectEqual(result.@"error", null) catch unreachable;
+ self.p = result.operator_ptr;
return self;
}
@@ -61,17 +63,17 @@ test "Opendal BDD test" {
// c_str does not have len field (.* is ptr)
.len = std.mem.len(testkit.content),
};
- const code = opendal.c.opendal_operator_blocking_write(testkit.p,
testkit.path, data);
- try testing.expectEqual(code, @intFromEnum(Code.OK));
+ const result = opendal.c.opendal_operator_blocking_write(testkit.p,
testkit.path, data);
+ try testing.expectEqual(result, null);
// The blocking file "test" should exist
var e: opendal.c.opendal_result_is_exist =
opendal.c.opendal_operator_is_exist(testkit.p, testkit.path);
- try testing.expectEqual(e.code, @intFromEnum(Code.OK));
+ try testing.expectEqual(e.@"error", null);
try testing.expect(e.is_exist);
// The blocking file "test" entry mode must be file
var s: opendal.c.opendal_result_stat =
opendal.c.opendal_operator_stat(testkit.p, testkit.path);
- try testing.expectEqual(s.code, @intFromEnum(Code.OK));
+ try testing.expectEqual(s.@"error", null);
var meta: [*c]opendal.c.opendal_metadata = s.meta;
try testing.expect(opendal.c.opendal_metadata_is_file(meta));
@@ -82,7 +84,7 @@ test "Opendal BDD test" {
// 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);
defer opendal.c.opendal_bytes_free(r.data);
- try testing.expect(r.code == @intFromEnum(Code.OK));
+ try testing.expect(r.@"error" == null);
try testing.expectEqual(std.mem.len(testkit.content), r.data.*.len);
var count: usize = 0;