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);
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));
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_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
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);
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_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));
tc_task_start(task);
TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_start(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));
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
/// an error response. It is equivalent to `Result<bool, ()>`.
/// cbindgen:prefix-with-name
/// cbindgen:rename-all=ScreamingSnakeCase
#[repr(C)]
pub enum TCResult {
True,
False,
Error,
Error = -1,
False = 0,
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 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
where
F: FnOnce(&Task) -> T,
@ -131,8 +134,9 @@ where
}
/// Utility function to get a mutable reference to the underlying Task. The
/// TCTask must be mutable.
fn wrap_mut<'a, T, F>(task: *mut TCTask, f: F) -> T
/// TCTask must be mutable. The inner function may use `?` syntax to return an
/// 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
F: FnOnce(&mut TaskMut) -> anyhow::Result<T>,
{
@ -145,8 +149,13 @@ where
TCTask::Mutable(ref mut t, _) => t,
TCTask::Invalid => unreachable!(),
};
match f(task) {
Ok(rv) => rv,
Err(e) => {
// TODO: add TCTask error handling, like replica
f(task).unwrap()
err_value
}
}
}
/// 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.
///
/// Returns false on error.
/// 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) -> bool {
wrap_mut(task, |task| {
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(true)
})
Ok(TCResult::True)
},
TCResult::Error,
)
}
/// Set a mutable task's description.
///
/// Returns false on error.
/// 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,
description: *mut TCString,
) -> bool {
) -> TCResult {
// SAFETY:
// - tcstring is not NULL (promised by caller)
// - caller is exclusive owner of tcstring (implicitly promised by caller)
let description = unsafe { TCString::from_arg(description) };
wrap_mut(task, |task| {
wrap_mut(
task,
|task| {
task.set_description(description.as_str()?.to_string())?;
Ok(true)
})
Ok(TCResult::True)
},
TCResult::Error,
)
}
// TODO: tc_task_set_description
@ -272,24 +289,32 @@ pub extern "C" fn tc_task_set_description<'a>(
/// Start a task.
///
/// TODO: error
/// 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) {
wrap_mut(task, |task| {
pub extern "C" fn tc_task_start<'a>(task: *mut TCTask) -> TCResult {
wrap_mut(
task,
|task| {
task.start()?;
Ok(())
})
Ok(TCResult::True)
},
TCResult::Error,
)
}
/// Stop a task.
///
/// TODO: error
/// 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) {
wrap_mut(task, |task| {
pub extern "C" fn tc_task_stop<'a>(task: *mut TCTask) -> TCResult {
wrap_mut(
task,
|task| {
task.stop()?;
Ok(())
})
Ok(TCResult::True)
},
TCResult::Error,
)
}
// TODO: tc_task_done

View file

@ -11,9 +11,9 @@
* an error response. It is equivalent to `Result<bool, ()>`.
*/
typedef enum TCResult {
TC_RESULT_TRUE,
TC_RESULT_FALSE,
TC_RESULT_ERROR,
TC_RESULT_ERROR = -1,
TC_RESULT_FALSE = 0,
TC_RESULT_TRUE = 1,
} TCResult;
/**
@ -258,30 +258,30 @@ bool tc_task_is_active(const struct TCTask *task);
/**
* 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.
*
* 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.
*
* 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.
*
* 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