diff --git a/integration-tests/src/bindings_tests/replica.c b/integration-tests/src/bindings_tests/replica.c index 756c9acfe..bf3cdaa43 100644 --- a/integration-tests/src/bindings_tests/replica.c +++ b/integration-tests/src/bindings_tests/replica.c @@ -23,8 +23,20 @@ static void test_replica_creation_disk(void) { static void test_replica_undo_empty(void) { TCReplica *rep = tc_replica_new_in_memory(); TEST_ASSERT_NULL(tc_replica_error(rep)); - int rv = tc_replica_undo(rep); - TEST_ASSERT_EQUAL(TC_RESULT_FALSE, rv); + int undone; + int rv = tc_replica_undo(rep, &undone); + TEST_ASSERT_EQUAL(TC_RESULT_OK, rv); + TEST_ASSERT_EQUAL(0, undone); + TEST_ASSERT_NULL(tc_replica_error(rep)); + tc_replica_free(rep); +} + +// When tc_replica_undo is passed NULL for undone_out, it still succeeds +static void test_replica_undo_empty_null_undone_out(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep)); + int rv = tc_replica_undo(rep, NULL); + TEST_ASSERT_EQUAL(TC_RESULT_OK, rv); TEST_ASSERT_NULL(tc_replica_error(rep)); tc_replica_free(rep); } @@ -110,6 +122,7 @@ int replica_tests(void) { RUN_TEST(test_replica_creation); RUN_TEST(test_replica_creation_disk); RUN_TEST(test_replica_undo_empty); + RUN_TEST(test_replica_undo_empty_null_undone_out); RUN_TEST(test_replica_task_creation); RUN_TEST(test_replica_task_import); RUN_TEST(test_replica_get_task_not_found); diff --git a/integration-tests/src/bindings_tests/task.c b/integration-tests/src/bindings_tests/task.c index 94024bc76..9ca537760 100644 --- a/integration-tests/src/bindings_tests/task.c +++ b/integration-tests/src/bindings_tests/task.c @@ -41,7 +41,7 @@ static void test_task_free_mutable_task(void) { TCUuid uuid = tc_task_get_uuid(task); tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_set_status(task, TC_STATUS_DELETED)); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_status(task, TC_STATUS_DELETED)); TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); tc_task_free(task); // implicitly converts to immut @@ -68,7 +68,7 @@ static void test_task_get_set_status(void) { TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_set_status(task, TC_STATUS_DELETED)); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_status(task, TC_STATUS_DELETED)); TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while mut tc_task_to_immut(task); TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while immut @@ -92,7 +92,7 @@ static void test_task_get_set_description(void) { TCString *desc; tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_set_description(task, tc_string_borrow("updated"))); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_description(task, tc_string_borrow("updated"))); TEST_ASSERT_TRUE(desc = tc_task_get_description(task)); TEST_ASSERT_NOT_NULL(desc); @@ -127,9 +127,9 @@ static void test_task_start_stop_is_active(void) { tc_task_to_mut(task, rep); TEST_ASSERT_FALSE(tc_task_is_active(task)); - TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_start(task)); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_start(task)); TEST_ASSERT_TRUE(tc_task_is_active(task)); - TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_stop(task)); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_stop(task)); TEST_ASSERT_FALSE(tc_task_is_active(task)); tc_task_free(task); @@ -149,7 +149,7 @@ static void task_task_add_tag(void) { tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_add_tag(task, tc_string_borrow("next"))); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_tag(task, tc_string_borrow("next"))); TEST_ASSERT_NULL(tc_task_error(task)); // invalid - synthetic tag diff --git a/lib/src/replica.rs b/lib/src/replica.rs index 7ec619495..0d2a5af4b 100644 --- a/lib/src/replica.rs +++ b/lib/src/replica.rs @@ -6,6 +6,9 @@ use taskchampion::{Replica, StorageConfig, Uuid}; /// for querying and modifying that data. /// /// TCReplicas are not threadsafe. +/// +/// When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then +/// `tc_replica_error` will return the error message. pub struct TCReplica { /// The wrapped Replica inner: Replica, @@ -213,18 +216,21 @@ pub extern "C" fn tc_replica_import_task_with_uuid( /// Undo local operations until the most recent UndoPoint. /// -/// Returns TC_RESULT_TRUE if an undo occurred, TC_RESULT_FALSE if there are no operations -/// to be undone, or TC_RESULT_ERROR on error. +/// If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if +/// there are no operations that can be done. #[no_mangle] -pub extern "C" fn tc_replica_undo<'a>(rep: *mut TCReplica) -> TCResult { +pub extern "C" fn tc_replica_undo<'a>(rep: *mut TCReplica, undone_out: *mut i32) -> TCResult { wrap( rep, |rep| { - Ok(if rep.undo()? { - TCResult::True - } else { - TCResult::False - }) + let undone = if rep.undo()? { 1 } else { 0 }; + if !undone_out.is_null() { + // SAFETY: + // - undone_out is not NULL (just checked) + // - undone_out is properly aligned (implicitly promised by caller) + unsafe { *undone_out = undone }; + } + Ok(TCResult::Ok) }, TCResult::Error, ) diff --git a/lib/src/result.rs b/lib/src/result.rs index 12f5e3e6a..a7d53ea8d 100644 --- a/lib/src/result.rs +++ b/lib/src/result.rs @@ -1,11 +1,9 @@ -// TODO: make true = 1, false = 0, error = -1 -/// A result combines a boolean success value with -/// an error response. It is equivalent to `Result`. +/// A result from a TC operation. Typically if this value is TC_RESULT_ERROR, +/// the associated object's `tc_.._error` method will return an error message. /// cbindgen:prefix-with-name /// cbindgen:rename-all=ScreamingSnakeCase -#[repr(C)] +#[repr(i32)] pub enum TCResult { Error = -1, - False = 0, - True = 1, + Ok = 0, } diff --git a/lib/src/task.rs b/lib/src/task.rs index 870fc0f91..ab79079a3 100644 --- a/lib/src/task.rs +++ b/lib/src/task.rs @@ -16,6 +16,11 @@ use taskchampion::{Tag, Task, TaskMut}; /// be used until it is freed or converted to a TaskMut. /// /// All `tc_task_..` functions taking a task as an argument require that it not be NULL. +/// +/// When a `tc_task_..` function that returns a TCResult returns TC_RESULT_ERROR, then +/// `tc_task_error` will return the error message. +/// +/// TCTasks are not threadsafe. pub struct TCTask { /// The wrapped Task or TaskMut inner: Inner, @@ -249,23 +254,19 @@ pub extern "C" fn tc_task_is_active<'a>(task: *mut TCTask) -> bool { // TODO: tc_task_get_modified /// Set a mutable task's status. -/// -/// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. #[no_mangle] pub extern "C" fn tc_task_set_status<'a>(task: *mut TCTask, status: TCStatus) -> TCResult { wrap_mut( task, |task| { task.set_status(status.into())?; - Ok(TCResult::True) + Ok(TCResult::Ok) }, TCResult::Error, ) } /// Set a mutable task's description. -/// -/// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. #[no_mangle] pub extern "C" fn tc_task_set_description<'a>( task: *mut TCTask, @@ -279,7 +280,7 @@ pub extern "C" fn tc_task_set_description<'a>( task, |task| { task.set_description(description.as_str()?.to_string())?; - Ok(TCResult::True) + Ok(TCResult::Ok) }, TCResult::Error, ) @@ -290,30 +291,26 @@ pub extern "C" fn tc_task_set_description<'a>( // TODO: tc_task_set_modified /// Start a task. -/// -/// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. #[no_mangle] pub extern "C" fn tc_task_start<'a>(task: *mut TCTask) -> TCResult { wrap_mut( task, |task| { task.start()?; - Ok(TCResult::True) + Ok(TCResult::Ok) }, TCResult::Error, ) } /// Stop a task. -/// -/// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. #[no_mangle] pub extern "C" fn tc_task_stop<'a>(task: *mut TCTask) -> TCResult { wrap_mut( task, |task| { task.stop()?; - Ok(TCResult::True) + Ok(TCResult::Ok) }, TCResult::Error, ) @@ -323,8 +320,6 @@ pub extern "C" fn tc_task_stop<'a>(task: *mut TCTask) -> TCResult { // TODO: tc_task_delete /// Add a tag to a mutable task. -/// -/// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. #[no_mangle] pub extern "C" fn tc_task_add_tag<'a>(task: *mut TCTask, tag: *mut TCString) -> TCResult { // SAFETY: @@ -337,7 +332,7 @@ pub extern "C" fn tc_task_add_tag<'a>(task: *mut TCTask, tag: *mut TCString) -> let tagstr = tcstring.as_str()?; let tag = Tag::from_str(tagstr)?; task.add_tag(&tag)?; - Ok(TCResult::True) + Ok(TCResult::Ok) }, TCResult::Error, ) diff --git a/lib/taskchampion.h b/lib/taskchampion.h index 33a710068..e2c3cef79 100644 --- a/lib/taskchampion.h +++ b/lib/taskchampion.h @@ -7,14 +7,20 @@ #define TC_UUID_STRING_BYTES 36 /** - * A result combines a boolean success value with - * an error response. It is equivalent to `Result`. + * A result from a TC operation. Typically if this value is TC_RESULT_ERROR, + * the associated object's `tc_.._error` method will return an error message. */ -typedef enum TCResult { +enum TCResult +#ifdef __cplusplus + : int32_t +#endif // __cplusplus + { TC_RESULT_ERROR = -1, - TC_RESULT_FALSE = 0, - TC_RESULT_TRUE = 1, -} TCResult; + TC_RESULT_OK = 0, +}; +#ifndef __cplusplus +typedef int32_t TCResult; +#endif // __cplusplus /** * The status of a task, as defined by the task data model. @@ -35,6 +41,9 @@ typedef enum TCStatus { * for querying and modifying that data. * * TCReplicas are not threadsafe. + * + * When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then + * `tc_replica_error` will return the error message. */ typedef struct TCReplica TCReplica; @@ -69,6 +78,11 @@ typedef struct TCString TCString; * be used until it is freed or converted to a TaskMut. * * All `tc_task_..` functions taking a task as an argument require that it not be NULL. + * + * When a `tc_task_..` function that returns a TCResult returns TC_RESULT_ERROR, then + * `tc_task_error` will return the error message. + * + * TCTasks are not threadsafe. */ typedef struct TCTask TCTask; @@ -127,10 +141,10 @@ struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TC /** * Undo local operations until the most recent UndoPoint. * - * Returns TC_RESULT_TRUE if an undo occurred, TC_RESULT_FALSE if there are no operations - * to be undone, or TC_RESULT_ERROR on error. + * If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if + * there are no operations that can be done. */ -enum TCResult tc_replica_undo(struct TCReplica *rep); +TCResult tc_replica_undo(struct TCReplica *rep, int32_t *undone_out); /** * Get the latest error for a replica, or NULL if the last operation succeeded. Subsequent calls @@ -257,38 +271,28 @@ bool tc_task_is_active(struct TCTask *task); /** * Set a mutable task's status. - * - * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. */ -enum TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status); +TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status); /** * Set a mutable task's description. - * - * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. */ -enum TCResult tc_task_set_description(struct TCTask *task, struct TCString *description); +TCResult tc_task_set_description(struct TCTask *task, struct TCString *description); /** * Start a task. - * - * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. */ -enum TCResult tc_task_start(struct TCTask *task); +TCResult tc_task_start(struct TCTask *task); /** * Stop a task. - * - * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. */ -enum TCResult tc_task_stop(struct TCTask *task); +TCResult tc_task_stop(struct TCTask *task); /** * Add a tag to a mutable task. - * - * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure. */ -enum TCResult tc_task_add_tag(struct TCTask *task, struct TCString *tag); +TCResult tc_task_add_tag(struct TCTask *task, struct TCString *tag); /** * Get the latest error for a task, or NULL if the last operation succeeded. Subsequent calls