add tc_task_add_tag and check errors

This commit is contained in:
Dustin J. Mitchell 2022-01-31 19:44:00 +00:00
parent ef0bb2ced4
commit ce45c1004c
3 changed files with 61 additions and 3 deletions

View file

@ -136,6 +136,35 @@ static void test_task_start_stop_is_active(void) {
tc_replica_free(rep); tc_replica_free(rep);
} }
// adding tags to a task works, and invalid tags are rejected
static void task_task_add_tag(void) {
TCReplica *rep = tc_replica_new_in_memory();
TEST_ASSERT_NULL(tc_replica_error(rep));
TCTask *task = tc_replica_new_task(
rep,
TC_STATUS_PENDING,
tc_string_borrow("my task"));
TEST_ASSERT_NOT_NULL(task);
tc_task_to_mut(task, rep);
TEST_ASSERT_EQUAL(TC_RESULT_TRUE, tc_task_add_tag(task, tc_string_borrow("next")));
// invalid - synthetic tag
TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("PENDING")));
// invald - not a valid tag string
TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("my tag")));
// invald - not utf-8
TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("\xf0\x28\x8c\x28")));
// TODO: check error messages
// TODO: test getting the tag
tc_task_free(task);
tc_replica_free(rep);
}
int task_tests(void) { int task_tests(void) {
UNITY_BEGIN(); UNITY_BEGIN();
// each test case above should be named here, in order. // each test case above should be named here, in order.
@ -144,5 +173,6 @@ int task_tests(void) {
RUN_TEST(test_task_get_set_status); RUN_TEST(test_task_get_set_status);
RUN_TEST(test_task_get_set_description); RUN_TEST(test_task_get_set_description);
RUN_TEST(test_task_start_stop_is_active); RUN_TEST(test_task_start_stop_is_active);
RUN_TEST(task_task_add_tag);
return UNITY_END(); return UNITY_END();
} }

View file

@ -2,7 +2,8 @@ use crate::{
replica::TCReplica, result::TCResult, status::TCStatus, string::TCString, uuid::TCUuid, replica::TCReplica, result::TCResult, status::TCStatus, string::TCString, uuid::TCUuid,
}; };
use std::ops::Deref; use std::ops::Deref;
use taskchampion::{Task, TaskMut}; use std::str::FromStr;
use taskchampion::{Tag, Task, TaskMut};
/// A task, as publicly exposed by this library. /// A task, as publicly exposed by this library.
/// ///
@ -282,7 +283,6 @@ pub extern "C" fn tc_task_set_description<'a>(
) )
} }
// TODO: tc_task_set_description
// TODO: tc_task_set_entry // TODO: tc_task_set_entry
// TODO: tc_task_set_wait // TODO: tc_task_set_wait
// TODO: tc_task_set_modified // TODO: tc_task_set_modified
@ -319,7 +319,28 @@ pub extern "C" fn tc_task_stop<'a>(task: *mut TCTask) -> TCResult {
// TODO: tc_task_done // TODO: tc_task_done
// TODO: tc_task_delete // TODO: tc_task_delete
// TODO: tc_task_add_tag
/// 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:
// - tcstring is not NULL (promised by caller)
// - caller is exclusive owner of tcstring (implicitly promised by caller)
let tcstring = unsafe { TCString::from_arg(tag) };
wrap_mut(
task,
|task| {
let tagstr = tcstring.as_str()?;
let tag = Tag::from_str(tagstr)?;
task.add_tag(&tag)?;
Ok(TCResult::True)
},
TCResult::Error,
)
}
// TODO: tc_task_remove_tag // TODO: tc_task_remove_tag
// TODO: tc_task_add_annotation // TODO: tc_task_add_annotation
// TODO: tc_task_remove_annotation // TODO: tc_task_remove_annotation

View file

@ -283,6 +283,13 @@ enum TCResult tc_task_start(struct TCTask *task);
*/ */
enum TCResult tc_task_stop(struct TCTask *task); enum 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);
/** /**
* 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
* returns, and must not be freed more than once. * returns, and must not be freed more than once.