allow task setters to return error values

This commit is contained in:
Dustin J. Mitchell 2022-01-31 19:34:21 +00:00
parent 8bd9605b25
commit ef0bb2ced4
4 changed files with 75 additions and 49 deletions

View file

@ -41,7 +41,7 @@ static void test_task_free_mutable_task(void) {
TCUuid uuid = tc_task_get_uuid(task); TCUuid uuid = tc_task_get_uuid(task);
tc_task_to_mut(task, rep); tc_task_to_mut(task, rep);
TEST_ASSERT_TRUE(tc_task_set_status(task, TC_STATUS_DELETED)); TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_set_status(task, TC_STATUS_DELETED));
TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task));
tc_task_free(task); // implicitly converts to immut 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)); TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task));
tc_task_to_mut(task, rep); tc_task_to_mut(task, rep);
TEST_ASSERT_TRUE(tc_task_set_status(task, TC_STATUS_DELETED)); TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_set_status(task, TC_STATUS_DELETED));
TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while mut TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while mut
tc_task_to_immut(task); tc_task_to_immut(task);
TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while immut 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; TCString *desc;
tc_task_to_mut(task, rep); tc_task_to_mut(task, rep);
tc_task_set_description(task, tc_string_borrow("updated")); TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_set_description(task, tc_string_borrow("updated")));
TEST_ASSERT_TRUE(desc = tc_task_get_description(task)); TEST_ASSERT_TRUE(desc = tc_task_get_description(task));
TEST_ASSERT_NOT_NULL(desc); TEST_ASSERT_NOT_NULL(desc);
@ -127,9 +127,9 @@ static void test_task_start_stop_is_active(void) {
tc_task_to_mut(task, rep); tc_task_to_mut(task, rep);
TEST_ASSERT_FALSE(tc_task_is_active(task)); TEST_ASSERT_FALSE(tc_task_is_active(task));
tc_task_start(task); TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_start(task));
TEST_ASSERT_TRUE(tc_task_is_active(task)); TEST_ASSERT_TRUE(tc_task_is_active(task));
tc_task_stop(task); TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_stop(task));
TEST_ASSERT_FALSE(tc_task_is_active(task)); TEST_ASSERT_FALSE(tc_task_is_active(task));
tc_task_free(task); tc_task_free(task);

View file

@ -1,10 +1,11 @@
// TODO: make true = 1, false = 0, error = -1
/// A result combines a boolean success value with /// A result combines a boolean success value with
/// an error response. It is equivalent to `Result<bool, ()>`. /// an error response. It is equivalent to `Result<bool, ()>`.
/// cbindgen:prefix-with-name /// cbindgen:prefix-with-name
/// cbindgen:rename-all=ScreamingSnakeCase /// cbindgen:rename-all=ScreamingSnakeCase
#[repr(C)] #[repr(C)]
pub enum TCResult { pub enum TCResult {
True, Error = -1,
False, False = 0,
Error, True = 1,
} }

View file

@ -1,4 +1,6 @@
use crate::{replica::TCReplica, status::TCStatus, string::TCString, uuid::TCUuid}; use crate::{
replica::TCReplica, result::TCResult, status::TCStatus, string::TCString, uuid::TCUuid,
};
use std::ops::Deref; use std::ops::Deref;
use taskchampion::{Task, TaskMut}; use taskchampion::{Task, TaskMut};
@ -113,7 +115,8 @@ impl From<Task> for TCTask {
} }
} }
/// Utility function to get a shared reference to the underlying Task. /// Utility function to get a shared reference to the underlying Task. All Task getters
/// are error-free, so this does not handle errors.
fn wrap<'a, T, F>(task: *const TCTask, f: F) -> T fn wrap<'a, T, F>(task: *const TCTask, f: F) -> T
where where
F: FnOnce(&Task) -> T, F: FnOnce(&Task) -> T,
@ -131,8 +134,9 @@ where
} }
/// Utility function to get a mutable reference to the underlying Task. The /// Utility function to get a mutable reference to the underlying Task. The
/// TCTask must be mutable. /// TCTask must be mutable. The inner function may use `?` syntax to return an
fn wrap_mut<'a, T, F>(task: *mut TCTask, f: F) -> T /// error, which will be represented with the `err_value` returned to C.
fn wrap_mut<'a, T, F>(task: *mut TCTask, f: F, err_value: T) -> T
where where
F: FnOnce(&mut TaskMut) -> anyhow::Result<T>, F: FnOnce(&mut TaskMut) -> anyhow::Result<T>,
{ {
@ -145,8 +149,13 @@ where
TCTask::Mutable(ref mut t, _) => t, TCTask::Mutable(ref mut t, _) => t,
TCTask::Invalid => unreachable!(), TCTask::Invalid => unreachable!(),
}; };
match f(task) {
Ok(rv) => rv,
Err(e) => {
// TODO: add TCTask error handling, like replica // TODO: add TCTask error handling, like replica
f(task).unwrap() err_value
}
}
} }
/// Convert an immutable task into a mutable task. /// Convert an immutable task into a mutable task.
@ -238,31 +247,39 @@ pub extern "C" fn tc_task_is_active<'a>(task: *const TCTask) -> bool {
/// Set a mutable task's status. /// Set a mutable task's status.
/// ///
/// Returns false on error. /// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure.
#[no_mangle] #[no_mangle]
pub extern "C" fn tc_task_set_status<'a>(task: *mut TCTask, status: TCStatus) -> bool { pub extern "C" fn tc_task_set_status<'a>(task: *mut TCTask, status: TCStatus) -> TCResult {
wrap_mut(task, |task| { wrap_mut(
task,
|task| {
task.set_status(status.into())?; task.set_status(status.into())?;
Ok(true) Ok(TCResult::True)
}) },
TCResult::Error,
)
} }
/// Set a mutable task's description. /// Set a mutable task's description.
/// ///
/// Returns false on error. /// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure.
#[no_mangle] #[no_mangle]
pub extern "C" fn tc_task_set_description<'a>( pub extern "C" fn tc_task_set_description<'a>(
task: *mut TCTask, task: *mut TCTask,
description: *mut TCString, description: *mut TCString,
) -> bool { ) -> TCResult {
// SAFETY: // SAFETY:
// - tcstring is not NULL (promised by caller) // - tcstring is not NULL (promised by caller)
// - caller is exclusive owner of tcstring (implicitly promised by caller) // - caller is exclusive owner of tcstring (implicitly promised by caller)
let description = unsafe { TCString::from_arg(description) }; let description = unsafe { TCString::from_arg(description) };
wrap_mut(task, |task| { wrap_mut(
task,
|task| {
task.set_description(description.as_str()?.to_string())?; task.set_description(description.as_str()?.to_string())?;
Ok(true) Ok(TCResult::True)
}) },
TCResult::Error,
)
} }
// TODO: tc_task_set_description // TODO: tc_task_set_description
@ -272,24 +289,32 @@ pub extern "C" fn tc_task_set_description<'a>(
/// Start a task. /// Start a task.
/// ///
/// TODO: error /// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure.
#[no_mangle] #[no_mangle]
pub extern "C" fn tc_task_start<'a>(task: *mut TCTask) { pub extern "C" fn tc_task_start<'a>(task: *mut TCTask) -> TCResult {
wrap_mut(task, |task| { wrap_mut(
task,
|task| {
task.start()?; task.start()?;
Ok(()) Ok(TCResult::True)
}) },
TCResult::Error,
)
} }
/// Stop a task. /// Stop a task.
/// ///
/// TODO: error /// Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure.
#[no_mangle] #[no_mangle]
pub extern "C" fn tc_task_stop<'a>(task: *mut TCTask) { pub extern "C" fn tc_task_stop<'a>(task: *mut TCTask) -> TCResult {
wrap_mut(task, |task| { wrap_mut(
task,
|task| {
task.stop()?; task.stop()?;
Ok(()) Ok(TCResult::True)
}) },
TCResult::Error,
)
} }
// TODO: tc_task_done // TODO: tc_task_done

View file

@ -11,9 +11,9 @@
* an error response. It is equivalent to `Result<bool, ()>`. * an error response. It is equivalent to `Result<bool, ()>`.
*/ */
typedef enum TCResult { typedef enum TCResult {
TC_RESULT_TRUE, TC_RESULT_ERROR = -1,
TC_RESULT_FALSE, TC_RESULT_FALSE = 0,
TC_RESULT_ERROR, TC_RESULT_TRUE = 1,
} TCResult; } TCResult;
/** /**
@ -258,30 +258,30 @@ bool tc_task_is_active(const struct TCTask *task);
/** /**
* Set a mutable task's status. * Set a mutable task's status.
* *
* Returns false on error. * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure.
*/ */
bool tc_task_set_status(struct TCTask *task, enum TCStatus status); enum TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status);
/** /**
* Set a mutable task's description. * Set a mutable task's description.
* *
* Returns false on error. * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure.
*/ */
bool tc_task_set_description(struct TCTask *task, struct TCString *description); enum TCResult tc_task_set_description(struct TCTask *task, struct TCString *description);
/** /**
* Start a task. * Start a task.
* *
* TODO: error * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure.
*/ */
void tc_task_start(struct TCTask *task); enum TCResult tc_task_start(struct TCTask *task);
/** /**
* Stop a task. * Stop a task.
* *
* TODO: error * Returns TC_RESULT_TRUE on success and TC_RESULT_ERROR on failure.
*/ */
void tc_task_stop(struct TCTask *task); enum TCResult tc_task_stop(struct TCTask *task);
/** /**
* Free a task. The given task must not be NULL. The task must not be used after this function * Free a task. The given task must not be NULL. The task must not be used after this function