From ce45c1004c678dff7c03619956248216e87f007d Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 31 Jan 2022 19:44:00 +0000 Subject: [PATCH] add tc_task_add_tag and check errors --- integration-tests/src/bindings_tests/task.c | 30 +++++++++++++++++++++ lib/src/task.rs | 27 ++++++++++++++++--- lib/taskchampion.h | 7 +++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/integration-tests/src/bindings_tests/task.c b/integration-tests/src/bindings_tests/task.c index 0a80bfaaf..bceb657b0 100644 --- a/integration-tests/src/bindings_tests/task.c +++ b/integration-tests/src/bindings_tests/task.c @@ -136,6 +136,35 @@ static void test_task_start_stop_is_active(void) { 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) { UNITY_BEGIN(); // 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_description); RUN_TEST(test_task_start_stop_is_active); + RUN_TEST(task_task_add_tag); return UNITY_END(); } diff --git a/lib/src/task.rs b/lib/src/task.rs index 531f33173..365bd88af 100644 --- a/lib/src/task.rs +++ b/lib/src/task.rs @@ -2,7 +2,8 @@ use crate::{ replica::TCReplica, result::TCResult, status::TCStatus, string::TCString, uuid::TCUuid, }; 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. /// @@ -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_wait // 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_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_add_annotation // TODO: tc_task_remove_annotation diff --git a/lib/taskchampion.h b/lib/taskchampion.h index 8330a1dbe..d2853484e 100644 --- a/lib/taskchampion.h +++ b/lib/taskchampion.h @@ -283,6 +283,13 @@ enum TCResult tc_task_start(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 * returns, and must not be freed more than once.