mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-24 18:06:42 +02:00
switch to PassByValue and PassByPointer traits
This commit is contained in:
parent
a46a9d587a
commit
23ba6a57b3
7 changed files with 187 additions and 192 deletions
|
@ -1,4 +1,5 @@
|
||||||
use crate::string::TCString;
|
use crate::string::TCString;
|
||||||
|
use crate::traits::*;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
// TODO: generalize to TCStrings?
|
// TODO: generalize to TCStrings?
|
||||||
|
@ -88,10 +89,8 @@ pub extern "C" fn tc_tags_free<'a>(tctags: *mut TCTags) {
|
||||||
|
|
||||||
// drop each contained string
|
// drop each contained string
|
||||||
for tcstring in vec.drain(..) {
|
for tcstring in vec.drain(..) {
|
||||||
// SAFETY:
|
// SAFETY: see TCString docstring
|
||||||
// - the pointer is not NULL (as created by TCString::new)
|
drop(unsafe { TCString::take_from_arg(tcstring.as_ptr()) });
|
||||||
// - C does not expect the string's lifetime to exceed this function
|
|
||||||
drop(unsafe { TCString::from_arg(tcstring.as_ptr()) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(vec);
|
drop(vec);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#![warn(unsafe_op_in_unsafe_fn)]
|
#![warn(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
mod traits;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub mod arrays;
|
pub mod arrays;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::traits::*;
|
||||||
use crate::util::err_to_tcstring;
|
use crate::util::err_to_tcstring;
|
||||||
use crate::{result::TCResult, status::TCStatus, string::TCString, task::TCTask, uuid::TCUuid};
|
use crate::{result::TCResult, status::TCStatus, string::TCString, task::TCTask, uuid::TCUuid};
|
||||||
use taskchampion::{Replica, StorageConfig, Uuid};
|
use taskchampion::{Replica, StorageConfig, Uuid};
|
||||||
|
@ -5,10 +6,25 @@ use taskchampion::{Replica, StorageConfig, Uuid};
|
||||||
/// A replica represents an instance of a user's task data, providing an easy interface
|
/// A replica represents an instance of a user's task data, providing an easy interface
|
||||||
/// for querying and modifying that data.
|
/// for querying and modifying that data.
|
||||||
///
|
///
|
||||||
/// TCReplicas are not threadsafe.
|
/// # Error Handling
|
||||||
///
|
///
|
||||||
/// When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then
|
/// When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then
|
||||||
/// `tc_replica_error` will return the error message.
|
/// `tc_replica_error` will return the error message.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `*TCReplica` returned from `tc_replica_new…` functions is owned by the caller and
|
||||||
|
/// must later be freed to avoid a memory leak.
|
||||||
|
///
|
||||||
|
/// Any function taking a `*TCReplica` requires:
|
||||||
|
/// - the pointer must not be NUL;
|
||||||
|
/// - the pointer must be one previously returned from a tc_… function;
|
||||||
|
/// - the memory referenced by the pointer must never be modified by C code; and
|
||||||
|
/// - except for `tc_replica_free`, ownership of a `*TCReplica` remains with the caller.
|
||||||
|
///
|
||||||
|
/// Once passed to `tc_replica_free`, a `*TCReplica` becmes invalid and must not be used again.
|
||||||
|
///
|
||||||
|
/// TCReplicas are not threadsafe.
|
||||||
pub struct TCReplica {
|
pub struct TCReplica {
|
||||||
/// The wrapped Replica
|
/// The wrapped Replica
|
||||||
inner: Replica,
|
inner: Replica,
|
||||||
|
@ -20,37 +36,9 @@ pub struct TCReplica {
|
||||||
error: Option<TCString<'static>>,
|
error: Option<TCString<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PassByPointer for TCReplica {}
|
||||||
|
|
||||||
impl TCReplica {
|
impl TCReplica {
|
||||||
/// Borrow a TCReplica from C as an argument.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The pointer must not be NULL. It is the caller's responsibility to ensure that the
|
|
||||||
/// lifetime assigned to the reference and the lifetime of the TCReplica itself do not outlive
|
|
||||||
/// the lifetime promised by C.
|
|
||||||
pub(crate) unsafe fn from_arg_ref<'a>(tcreplica: *mut TCReplica) -> &'a mut Self {
|
|
||||||
debug_assert!(!tcreplica.is_null());
|
|
||||||
// SAFETY: see doc comment
|
|
||||||
unsafe { &mut *tcreplica }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Take a TCReplica from C as an argument.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The pointer must not be NULL and must point to a valid replica. The pointer becomes
|
|
||||||
/// invalid before this function returns and must not be used afterward.
|
|
||||||
pub(crate) unsafe fn from_arg(tcreplica: *mut TCReplica) -> Self {
|
|
||||||
debug_assert!(!tcreplica.is_null());
|
|
||||||
// SAFETY: see doc comment
|
|
||||||
unsafe { *Box::from_raw(tcreplica) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert this to a return value for handing off to C.
|
|
||||||
pub(crate) fn return_val(self) -> *mut TCReplica {
|
|
||||||
Box::into_raw(Box::new(self))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably borrow the inner Replica
|
/// Mutably borrow the inner Replica
|
||||||
pub(crate) fn borrow_mut(&mut self) -> &mut Replica {
|
pub(crate) fn borrow_mut(&mut self) -> &mut Replica {
|
||||||
if self.mut_borrowed {
|
if self.mut_borrowed {
|
||||||
|
@ -85,10 +73,8 @@ fn wrap<'a, T, F>(rep: *mut TCReplica, f: F, err_value: T) -> T
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Replica) -> anyhow::Result<T>,
|
F: FnOnce(&mut Replica) -> anyhow::Result<T>,
|
||||||
{
|
{
|
||||||
// SAFETY:
|
// SAFETY: see type docstring
|
||||||
// - rep is not null (promised by caller)
|
let rep: &'a mut TCReplica = unsafe { TCReplica::from_arg_ref_mut(rep) };
|
||||||
// - rep outlives 'a (promised by caller)
|
|
||||||
let rep: &'a mut TCReplica = unsafe { TCReplica::from_arg_ref(rep) };
|
|
||||||
if rep.mut_borrowed {
|
if rep.mut_borrowed {
|
||||||
panic!("replica is borrowed and cannot be used");
|
panic!("replica is borrowed and cannot be used");
|
||||||
}
|
}
|
||||||
|
@ -109,21 +95,19 @@ pub extern "C" fn tc_replica_new_in_memory() -> *mut TCReplica {
|
||||||
let storage = StorageConfig::InMemory
|
let storage = StorageConfig::InMemory
|
||||||
.into_storage()
|
.into_storage()
|
||||||
.expect("in-memory always succeeds");
|
.expect("in-memory always succeeds");
|
||||||
TCReplica::from(Replica::new(storage)).return_val()
|
// SAFETY: see type docstring
|
||||||
|
unsafe { TCReplica::from(Replica::new(storage)).return_val() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new TCReplica with an on-disk database having the given filename. The filename must
|
/// Create a new TCReplica with an on-disk database having the given filename. On error, a string
|
||||||
/// not be NULL. On error, a string is written to the `error_out` parameter (if it is not NULL) and
|
/// is written to the `error_out` parameter (if it is not NULL) and NULL is returned.
|
||||||
/// NULL is returned.
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_replica_new_on_disk<'a>(
|
pub extern "C" fn tc_replica_new_on_disk<'a>(
|
||||||
path: *mut TCString,
|
path: *mut TCString,
|
||||||
error_out: *mut *mut TCString,
|
error_out: *mut *mut TCString,
|
||||||
) -> *mut TCReplica {
|
) -> *mut TCReplica {
|
||||||
// SAFETY:
|
// SAFETY: see TCString docstring
|
||||||
// - tcstring is not NULL (promised by caller)
|
let path = unsafe { TCString::take_from_arg(path) };
|
||||||
// - caller is exclusive owner of tcstring (implicitly promised by caller)
|
|
||||||
let path = unsafe { TCString::from_arg(path) };
|
|
||||||
let storage_res = StorageConfig::OnDisk {
|
let storage_res = StorageConfig::OnDisk {
|
||||||
taskdb_dir: path.to_path_buf(),
|
taskdb_dir: path.to_path_buf(),
|
||||||
}
|
}
|
||||||
|
@ -141,7 +125,8 @@ pub extern "C" fn tc_replica_new_on_disk<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TCReplica::from(Replica::new(storage)).return_val()
|
// SAFETY: see type docstring
|
||||||
|
unsafe { TCReplica::from(Replica::new(storage)).return_val() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: tc_replica_all_tasks
|
// TODO: tc_replica_all_tasks
|
||||||
|
@ -153,11 +138,11 @@ pub extern "C" fn tc_replica_new_on_disk<'a>(
|
||||||
/// Returns NULL when the task does not exist, and on error. Consult tc_replica_error
|
/// Returns NULL when the task does not exist, and on error. Consult tc_replica_error
|
||||||
/// to distinguish the two conditions.
|
/// to distinguish the two conditions.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_replica_get_task(rep: *mut TCReplica, uuid: TCUuid) -> *mut TCTask {
|
pub extern "C" fn tc_replica_get_task(rep: *mut TCReplica, tcuuid: TCUuid) -> *mut TCTask {
|
||||||
wrap(
|
wrap(
|
||||||
rep,
|
rep,
|
||||||
|rep| {
|
|rep| {
|
||||||
let uuid: Uuid = uuid.into();
|
let uuid = Uuid::from_arg(tcuuid);
|
||||||
if let Some(task) = rep.get_task(uuid)? {
|
if let Some(task) = rep.get_task(uuid)? {
|
||||||
Ok(TCTask::from(task).return_val())
|
Ok(TCTask::from(task).return_val())
|
||||||
} else {
|
} else {
|
||||||
|
@ -170,8 +155,6 @@ pub extern "C" fn tc_replica_get_task(rep: *mut TCReplica, uuid: TCUuid) -> *mut
|
||||||
|
|
||||||
/// Create a new task. The task must not already exist.
|
/// Create a new task. The task must not already exist.
|
||||||
///
|
///
|
||||||
/// The description must not be NULL.
|
|
||||||
///
|
|
||||||
/// Returns the task, or NULL on error.
|
/// Returns the task, or NULL on error.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_replica_new_task(
|
pub extern "C" fn tc_replica_new_task(
|
||||||
|
@ -179,10 +162,8 @@ pub extern "C" fn tc_replica_new_task(
|
||||||
status: TCStatus,
|
status: TCStatus,
|
||||||
description: *mut TCString,
|
description: *mut TCString,
|
||||||
) -> *mut TCTask {
|
) -> *mut TCTask {
|
||||||
// SAFETY:
|
// SAFETY: see TCString docstring
|
||||||
// - tcstring is not NULL (promised by caller)
|
let description = unsafe { TCString::take_from_arg(description) };
|
||||||
// - caller is exclusive owner of tcstring (implicitly promised by caller)
|
|
||||||
let description = unsafe { TCString::from_arg(description) };
|
|
||||||
wrap(
|
wrap(
|
||||||
rep,
|
rep,
|
||||||
|rep| {
|
|rep| {
|
||||||
|
@ -199,12 +180,12 @@ pub extern "C" fn tc_replica_new_task(
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_replica_import_task_with_uuid(
|
pub extern "C" fn tc_replica_import_task_with_uuid(
|
||||||
rep: *mut TCReplica,
|
rep: *mut TCReplica,
|
||||||
uuid: TCUuid,
|
tcuuid: TCUuid,
|
||||||
) -> *mut TCTask {
|
) -> *mut TCTask {
|
||||||
wrap(
|
wrap(
|
||||||
rep,
|
rep,
|
||||||
|rep| {
|
|rep| {
|
||||||
let uuid: Uuid = uuid.into();
|
let uuid = Uuid::from_arg(tcuuid);
|
||||||
let task = rep.import_task_with_uuid(uuid)?;
|
let task = rep.import_task_with_uuid(uuid)?;
|
||||||
Ok(TCTask::from(task).return_val())
|
Ok(TCTask::from(task).return_val())
|
||||||
},
|
},
|
||||||
|
@ -241,12 +222,11 @@ pub extern "C" fn tc_replica_undo<'a>(rep: *mut TCReplica, undone_out: *mut i32)
|
||||||
/// returned string.
|
/// returned string.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_replica_error<'a>(rep: *mut TCReplica) -> *mut TCString<'static> {
|
pub extern "C" fn tc_replica_error<'a>(rep: *mut TCReplica) -> *mut TCString<'static> {
|
||||||
// SAFETY:
|
// SAFETY: see type docstring
|
||||||
// - rep is not null (promised by caller)
|
let rep: &'a mut TCReplica = unsafe { TCReplica::from_arg_ref_mut(rep) };
|
||||||
// - rep outlives 'a (promised by caller)
|
|
||||||
let rep: &'a mut TCReplica = unsafe { TCReplica::from_arg_ref(rep) };
|
|
||||||
if let Some(tcstring) = rep.error.take() {
|
if let Some(tcstring) = rep.error.take() {
|
||||||
tcstring.return_val()
|
// SAFETY: see TCString docstring
|
||||||
|
unsafe { tcstring.return_val() }
|
||||||
} else {
|
} else {
|
||||||
std::ptr::null_mut()
|
std::ptr::null_mut()
|
||||||
}
|
}
|
||||||
|
@ -256,10 +236,8 @@ pub extern "C" fn tc_replica_error<'a>(rep: *mut TCReplica) -> *mut TCString<'st
|
||||||
/// more than once.
|
/// more than once.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_replica_free(rep: *mut TCReplica) {
|
pub extern "C" fn tc_replica_free(rep: *mut TCReplica) {
|
||||||
// SAFETY:
|
// SAFETY: see type docstring
|
||||||
// - rep is not NULL (promised by caller)
|
let replica = unsafe { TCReplica::take_from_arg(rep) };
|
||||||
// - caller will not use the TCReplica after this (promised by caller)
|
|
||||||
let replica = unsafe { TCReplica::from_arg(rep) };
|
|
||||||
if replica.mut_borrowed {
|
if replica.mut_borrowed {
|
||||||
panic!("replica is borrowed and cannot be freed");
|
panic!("replica is borrowed and cannot be freed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,39 @@
|
||||||
|
use crate::traits::*;
|
||||||
use std::ffi::{CStr, CString, OsStr};
|
use std::ffi::{CStr, CString, OsStr};
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
/// TCString supports passing strings into and out of the TaskChampion API. A string can contain
|
/// TCString supports passing strings into and out of the TaskChampion API.
|
||||||
/// embedded NUL characters. Strings containing such embedded NULs cannot be represented as a "C
|
|
||||||
/// string" and must be accessed using `tc_string_content_and_len` and `tc_string_clone_with_len`.
|
|
||||||
/// In general, these two functions should be used for handling arbitrary data, while more
|
|
||||||
/// convenient forms may be used where embedded NUL characters are impossible, such as in static
|
|
||||||
/// strings.
|
|
||||||
///
|
///
|
||||||
/// Rust expects all strings to be UTF-8, and API functions will fail if given a TCString
|
/// # Rust Strings and C Strings
|
||||||
/// containing invalid UTF-8.
|
|
||||||
///
|
///
|
||||||
/// Unless specified otherwise, functions in this API take ownership of a TCString when it is given
|
/// A Rust string can contain embedded NUL characters, while C considers such a character to mark
|
||||||
/// as a function argument, and free the string before returning. Callers must not use or free
|
/// the end of a string. Strings containing embedded NULs cannot be represented as a "C string"
|
||||||
/// strings after passing them to such API functions.
|
/// and must be accessed using `tc_string_content_and_len` and `tc_string_clone_with_len`. In
|
||||||
|
/// general, these two functions should be used for handling arbitrary data, while more convenient
|
||||||
|
/// forms may be used where embedded NUL characters are impossible, such as in static strings.
|
||||||
///
|
///
|
||||||
/// When a TCString appears as a return value or output argument, it is the responsibility of the
|
/// # UTF-8
|
||||||
/// caller to free the string.
|
///
|
||||||
|
/// TaskChampion expects all strings to be valid UTF-8. `tc_string_…` functions will fail if given
|
||||||
|
/// a `*TCString` containing invalid UTF-8.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// When a `*TCString` appears as a return value or output argument, ownership is passed to the
|
||||||
|
/// caller. The caller must pass that ownerhsip back to another function or free the string.
|
||||||
|
///
|
||||||
|
/// Any function taking a `*TCReplica` requires:
|
||||||
|
/// - the pointer must not be NUL;
|
||||||
|
/// - the pointer must be one previously returned from a tc_… function; and
|
||||||
|
/// - the memory referenced by the pointer must never be modified by C code.
|
||||||
|
///
|
||||||
|
/// Unless specified otherwise, TaskChampion functions take ownership of a `*TCString` when it is
|
||||||
|
/// given as a function argument, and the pointer is invalid when the function returns. Callers
|
||||||
|
/// must not use or free TCStrings after passing them to such API functions.
|
||||||
|
///
|
||||||
|
/// TCStrings are not threadsafe.
|
||||||
pub enum TCString<'a> {
|
pub enum TCString<'a> {
|
||||||
CString(CString),
|
CString(CString),
|
||||||
CStr(&'a CStr),
|
CStr(&'a CStr),
|
||||||
|
@ -39,36 +54,9 @@ impl<'a> Default for TCString<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> PassByPointer for TCString<'a> {}
|
||||||
|
|
||||||
impl<'a> TCString<'a> {
|
impl<'a> TCString<'a> {
|
||||||
/// Take a TCString from C as an argument.
|
|
||||||
///
|
|
||||||
/// C callers generally expect TC functions to take ownership of a string, which is what this
|
|
||||||
/// function does.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The pointer must not be NULL. It is the caller's responsibility to ensure that the
|
|
||||||
/// lifetime assigned to the reference and the lifetime of the TCString itself do not outlive
|
|
||||||
/// the lifetime promised by C.
|
|
||||||
pub(crate) unsafe fn from_arg(tcstring: *mut TCString<'a>) -> Self {
|
|
||||||
debug_assert!(!tcstring.is_null());
|
|
||||||
// SAFETY: see docstring
|
|
||||||
unsafe { *(Box::from_raw(tcstring)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Borrow a TCString from C as an argument.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The pointer must not be NULL. It is the caller's responsibility to ensure that the
|
|
||||||
/// lifetime assigned to the reference and the lifetime of the TCString itself do not outlive
|
|
||||||
/// the lifetime promised by C.
|
|
||||||
pub(crate) unsafe fn from_arg_ref(tcstring: *mut TCString<'a>) -> &'a mut Self {
|
|
||||||
debug_assert!(!tcstring.is_null());
|
|
||||||
// SAFETY: see docstring
|
|
||||||
unsafe { &mut *tcstring }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a regular Rust &str for this value.
|
/// Get a regular Rust &str for this value.
|
||||||
pub(crate) fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
|
pub(crate) fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -95,11 +83,6 @@ impl<'a> TCString<'a> {
|
||||||
let path: &OsStr = OsStr::from_bytes(self.as_bytes());
|
let path: &OsStr = OsStr::from_bytes(self.as_bytes());
|
||||||
path.to_os_string().into()
|
path.to_os_string().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this to a return value for handing off to C.
|
|
||||||
pub(crate) fn return_val(self) -> *mut TCString<'a> {
|
|
||||||
Box::into_raw(Box::new(self))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<String> for TCString<'a> {
|
impl<'a> From<String> for TCString<'a> {
|
||||||
|
@ -137,7 +120,8 @@ pub extern "C" fn tc_string_borrow(cstr: *const libc::c_char) -> *mut TCString<'
|
||||||
// - cstr contains a valid NUL terminator (promised by caller)
|
// - cstr contains a valid NUL terminator (promised by caller)
|
||||||
// - cstr's content will not change before it is destroyed (promised by caller)
|
// - cstr's content will not change before it is destroyed (promised by caller)
|
||||||
let cstr: &CStr = unsafe { CStr::from_ptr(cstr) };
|
let cstr: &CStr = unsafe { CStr::from_ptr(cstr) };
|
||||||
TCString::CStr(cstr).return_val()
|
// SAFETY: see docstring
|
||||||
|
unsafe { TCString::CStr(cstr).return_val() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new TCString by cloning the content of the given C string. The resulting TCString
|
/// Create a new TCString by cloning the content of the given C string. The resulting TCString
|
||||||
|
@ -151,7 +135,8 @@ pub extern "C" fn tc_string_clone(cstr: *const libc::c_char) -> *mut TCString<'s
|
||||||
// - cstr contains a valid NUL terminator (promised by caller)
|
// - cstr contains a valid NUL terminator (promised by caller)
|
||||||
// - cstr's content will not change before it is destroyed (by C convention)
|
// - cstr's content will not change before it is destroyed (by C convention)
|
||||||
let cstr: &CStr = unsafe { CStr::from_ptr(cstr) };
|
let cstr: &CStr = unsafe { CStr::from_ptr(cstr) };
|
||||||
TCString::CString(cstr.into()).return_val()
|
// SAFETY: see docstring
|
||||||
|
unsafe { TCString::CString(cstr.into()).return_val() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new TCString containing the given string with the given length. This allows creation
|
/// Create a new TCString containing the given string with the given length. This allows creation
|
||||||
|
@ -176,14 +161,15 @@ pub extern "C" fn tc_string_clone_with_len(
|
||||||
let vec = slice.to_vec();
|
let vec = slice.to_vec();
|
||||||
// try converting to a string, which is the only variant that can contain embedded NULs. If
|
// try converting to a string, which is the only variant that can contain embedded NULs. If
|
||||||
// the bytes are not valid utf-8, store that information for reporting later.
|
// the bytes are not valid utf-8, store that information for reporting later.
|
||||||
match String::from_utf8(vec) {
|
let tcstring = match String::from_utf8(vec) {
|
||||||
Ok(string) => TCString::String(string),
|
Ok(string) => TCString::String(string),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let (e, vec) = (e.utf8_error(), e.into_bytes());
|
let (e, vec) = (e.utf8_error(), e.into_bytes());
|
||||||
TCString::InvalidUtf8(e, vec)
|
TCString::InvalidUtf8(e, vec)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
.return_val()
|
// SAFETY: see docstring
|
||||||
|
unsafe { tcstring.return_val() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the content of the string as a regular C string. The given string must not be NULL. The
|
/// Get the content of the string as a regular C string. The given string must not be NULL. The
|
||||||
|
@ -200,11 +186,12 @@ pub extern "C" fn tc_string_content(tcstring: *mut TCString) -> *const libc::c_c
|
||||||
// - tcstring is not NULL (promised by caller)
|
// - tcstring is not NULL (promised by caller)
|
||||||
// - lifetime of tcstring outlives the lifetime of this function
|
// - lifetime of tcstring outlives the lifetime of this function
|
||||||
// - lifetime of tcstring outlives the lifetime of the returned pointer (promised by caller)
|
// - lifetime of tcstring outlives the lifetime of the returned pointer (promised by caller)
|
||||||
let tcstring = unsafe { TCString::from_arg_ref(tcstring) };
|
let tcstring = unsafe { TCString::from_arg_ref_mut(tcstring) };
|
||||||
|
|
||||||
// if we have a String, we need to consume it and turn it into
|
// if we have a String, we need to consume it and turn it into
|
||||||
// a CString.
|
// a CString.
|
||||||
if matches!(tcstring, TCString::String(_)) {
|
if matches!(tcstring, TCString::String(_)) {
|
||||||
|
// TODO: put this in a method
|
||||||
if let TCString::String(string) = std::mem::take(tcstring) {
|
if let TCString::String(string) = std::mem::take(tcstring) {
|
||||||
match CString::new(string) {
|
match CString::new(string) {
|
||||||
Ok(cstring) => {
|
Ok(cstring) => {
|
||||||
|
@ -237,7 +224,8 @@ pub extern "C" fn tc_string_content(tcstring: *mut TCString) -> *const libc::c_c
|
||||||
|
|
||||||
/// Get the content of the string as a pointer and length. The given string must not be NULL.
|
/// Get the content of the string as a pointer and length. The given string must not be NULL.
|
||||||
/// This function can return any string, even one including NUL bytes or invalid UTF-8. The
|
/// This function can return any string, even one including NUL bytes or invalid UTF-8. The
|
||||||
/// returned buffer is valid until the TCString is freed or passed to another TC API function.
|
/// returned buffer is valid until the TCString is freed or passed to another TaskChampio
|
||||||
|
/// function.
|
||||||
///
|
///
|
||||||
/// This function does _not_ take ownership of the TCString.
|
/// This function does _not_ take ownership of the TCString.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -274,5 +262,5 @@ pub extern "C" fn tc_string_free(tcstring: *mut TCString) {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - tcstring is not NULL (promised by caller)
|
// - tcstring is not NULL (promised by caller)
|
||||||
// - caller is exclusive owner of tcstring (promised by caller)
|
// - caller is exclusive owner of tcstring (promised by caller)
|
||||||
drop(unsafe { TCString::from_arg(tcstring) });
|
drop(unsafe { TCString::take_from_arg(tcstring) });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::traits::*;
|
||||||
use crate::util::err_to_tcstring;
|
use crate::util::err_to_tcstring;
|
||||||
use crate::{
|
use crate::{
|
||||||
arrays::TCTags, replica::TCReplica, result::TCResult, status::TCStatus, string::TCString,
|
arrays::TCTags, replica::TCReplica, result::TCResult, status::TCStatus, string::TCString,
|
||||||
|
@ -93,7 +94,8 @@ impl TCTask {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - tcreplica is not null (promised by caller)
|
// - tcreplica is not null (promised by caller)
|
||||||
// - tcreplica outlives the pointer in this variant (promised by caller)
|
// - tcreplica outlives the pointer in this variant (promised by caller)
|
||||||
let tcreplica_ref: &mut TCReplica = unsafe { TCReplica::from_arg_ref(tcreplica) };
|
let tcreplica_ref: &mut TCReplica =
|
||||||
|
unsafe { TCReplica::from_arg_ref_mut(tcreplica) };
|
||||||
let rep_ref = tcreplica_ref.borrow_mut();
|
let rep_ref = tcreplica_ref.borrow_mut();
|
||||||
Inner::Mutable(task.into_mut(rep_ref), tcreplica)
|
Inner::Mutable(task.into_mut(rep_ref), tcreplica)
|
||||||
}
|
}
|
||||||
|
@ -112,7 +114,8 @@ impl TCTask {
|
||||||
// - tcreplica is not null (promised by caller of to_mut, which created this
|
// - tcreplica is not null (promised by caller of to_mut, which created this
|
||||||
// variant)
|
// variant)
|
||||||
// - tcreplica is still alive (promised by caller of to_mut)
|
// - tcreplica is still alive (promised by caller of to_mut)
|
||||||
let tcreplica_ref: &mut TCReplica = unsafe { TCReplica::from_arg_ref(tcreplica) };
|
let tcreplica_ref: &mut TCReplica =
|
||||||
|
unsafe { TCReplica::from_arg_ref_mut(tcreplica) };
|
||||||
tcreplica_ref.release_borrow();
|
tcreplica_ref.release_borrow();
|
||||||
Inner::Immutable(task.into_immut())
|
Inner::Immutable(task.into_immut())
|
||||||
}
|
}
|
||||||
|
@ -246,7 +249,7 @@ pub extern "C" fn tc_task_to_immut<'a>(task: *mut TCTask) {
|
||||||
/// Get a task's UUID.
|
/// Get a task's UUID.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_task_get_uuid(task: *mut TCTask) -> TCUuid {
|
pub extern "C" fn tc_task_get_uuid(task: *mut TCTask) -> TCUuid {
|
||||||
wrap(task, |task| task.get_uuid().into())
|
wrap(task, |task| task.get_uuid().return_val())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a task's status.
|
/// Get a task's status.
|
||||||
|
@ -263,7 +266,8 @@ pub extern "C" fn tc_task_get_status<'a>(task: *mut TCTask) -> TCStatus {
|
||||||
pub extern "C" fn tc_task_get_description<'a>(task: *mut TCTask) -> *mut TCString<'static> {
|
pub extern "C" fn tc_task_get_description<'a>(task: *mut TCTask) -> *mut TCString<'static> {
|
||||||
wrap(task, |task| {
|
wrap(task, |task| {
|
||||||
let descr: TCString = task.get_description().into();
|
let descr: TCString = task.get_description().into();
|
||||||
descr.return_val()
|
// SAFETY: see TCString docstring
|
||||||
|
unsafe { descr.return_val() }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,13 +302,12 @@ pub extern "C" fn tc_task_is_active(task: *mut TCTask) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a task has the given tag. If the tag is invalid, this function will simply return
|
/// Check if a task has the given tag. If the tag is invalid, this function will simply return
|
||||||
/// false with no error from `tc_task_error`. The given tag must not be NULL.
|
/// false, with no error from `tc_task_error`.
|
||||||
|
// TODO: why no error??
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_task_has_tag<'a>(task: *mut TCTask, tag: *mut TCString) -> bool {
|
pub extern "C" fn tc_task_has_tag<'a>(task: *mut TCTask, tag: *mut TCString) -> bool {
|
||||||
// SAFETY:
|
// SAFETY: see TCString docstring
|
||||||
// - tcstring is not NULL (promised by caller)
|
let tcstring = unsafe { TCString::take_from_arg(tag) };
|
||||||
// - caller is exclusive owner of tcstring (implicitly promised by caller)
|
|
||||||
let tcstring = unsafe { TCString::from_arg(tag) };
|
|
||||||
wrap(task, |task| {
|
wrap(task, |task| {
|
||||||
if let Ok(tag) = Tag::try_from(tcstring) {
|
if let Ok(tag) = Tag::try_from(tcstring) {
|
||||||
task.has_tag(&tag)
|
task.has_tag(&tag)
|
||||||
|
@ -321,7 +324,8 @@ pub extern "C" fn tc_task_get_tags<'a>(task: *mut TCTask) -> TCTags {
|
||||||
let tcstrings: Vec<NonNull<TCString<'static>>> = task
|
let tcstrings: Vec<NonNull<TCString<'static>>> = task
|
||||||
.get_tags()
|
.get_tags()
|
||||||
.map(|t| {
|
.map(|t| {
|
||||||
let t_ptr = TCString::from(t.as_ref()).return_val();
|
// SAFETY: see TCString docstring
|
||||||
|
let t_ptr = unsafe { TCString::from(t.as_ref()).return_val() };
|
||||||
// SAFETY: t_ptr was just created and is not NULL
|
// SAFETY: t_ptr was just created and is not NULL
|
||||||
unsafe { NonNull::new_unchecked(t_ptr) }
|
unsafe { NonNull::new_unchecked(t_ptr) }
|
||||||
})
|
})
|
||||||
|
@ -355,10 +359,8 @@ pub extern "C" fn tc_task_set_description<'a>(
|
||||||
task: *mut TCTask,
|
task: *mut TCTask,
|
||||||
description: *mut TCString,
|
description: *mut TCString,
|
||||||
) -> TCResult {
|
) -> TCResult {
|
||||||
// SAFETY:
|
// SAFETY: see TCString docstring
|
||||||
// - tcstring is not NULL (promised by caller)
|
let description = unsafe { TCString::take_from_arg(description) };
|
||||||
// - caller is exclusive owner of tcstring (implicitly promised by caller)
|
|
||||||
let description = unsafe { TCString::from_arg(description) };
|
|
||||||
wrap_mut(
|
wrap_mut(
|
||||||
task,
|
task,
|
||||||
|task| {
|
|task| {
|
||||||
|
@ -466,10 +468,8 @@ pub extern "C" fn tc_task_delete(task: *mut TCTask) -> TCResult {
|
||||||
/// Add a tag to a mutable task.
|
/// Add a tag to a mutable task.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: *mut TCString) -> TCResult {
|
pub extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: *mut TCString) -> TCResult {
|
||||||
// SAFETY:
|
// SAFETY: see TCString docstring
|
||||||
// - tcstring is not NULL (promised by caller)
|
let tcstring = unsafe { TCString::take_from_arg(tag) };
|
||||||
// - caller is exclusive owner of tcstring (implicitly promised by caller)
|
|
||||||
let tcstring = unsafe { TCString::from_arg(tag) };
|
|
||||||
wrap_mut(
|
wrap_mut(
|
||||||
task,
|
task,
|
||||||
|task| {
|
|task| {
|
||||||
|
@ -484,10 +484,8 @@ pub extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: *mut TCString) -> TCRe
|
||||||
/// Remove a tag from a mutable task.
|
/// Remove a tag from a mutable task.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_task_remove_tag(task: *mut TCTask, tag: *mut TCString) -> TCResult {
|
pub extern "C" fn tc_task_remove_tag(task: *mut TCTask, tag: *mut TCString) -> TCResult {
|
||||||
// SAFETY:
|
// SAFETY: see TCString docstring
|
||||||
// - tcstring is not NULL (promised by caller)
|
let tcstring = unsafe { TCString::take_from_arg(tag) };
|
||||||
// - caller is exclusive owner of tcstring (implicitly promised by caller)
|
|
||||||
let tcstring = unsafe { TCString::from_arg(tag) };
|
|
||||||
wrap_mut(
|
wrap_mut(
|
||||||
task,
|
task,
|
||||||
|task| {
|
|task| {
|
||||||
|
@ -516,7 +514,7 @@ pub extern "C" fn tc_task_error<'a>(task: *mut TCTask) -> *mut TCString<'static>
|
||||||
// - task outlives 'a (promised by caller)
|
// - task outlives 'a (promised by caller)
|
||||||
let task: &'a mut TCTask = unsafe { TCTask::from_arg_ref(task) };
|
let task: &'a mut TCTask = unsafe { TCTask::from_arg_ref(task) };
|
||||||
if let Some(tcstring) = task.error.take() {
|
if let Some(tcstring) = task.error.take() {
|
||||||
tcstring.return_val()
|
unsafe { tcstring.return_val() }
|
||||||
} else {
|
} else {
|
||||||
std::ptr::null_mut()
|
std::ptr::null_mut()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::string::TCString;
|
use crate::string::TCString;
|
||||||
|
use crate::traits::*;
|
||||||
use libc;
|
use libc;
|
||||||
use taskchampion::Uuid;
|
use taskchampion::Uuid;
|
||||||
|
|
||||||
|
@ -9,28 +10,30 @@ use taskchampion::Uuid;
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct TCUuid([u8; 16]);
|
pub struct TCUuid([u8; 16]);
|
||||||
|
|
||||||
impl From<Uuid> for TCUuid {
|
impl PassByValue for Uuid {
|
||||||
fn from(uuid: Uuid) -> TCUuid {
|
type CType = TCUuid;
|
||||||
TCUuid(*uuid.as_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TCUuid> for Uuid {
|
unsafe fn from_ctype(arg: TCUuid) -> Self {
|
||||||
fn from(uuid: TCUuid) -> Uuid {
|
// SAFETY:
|
||||||
Uuid::from_bytes(uuid.0)
|
// - any 16-byte value is a valid Uuid
|
||||||
|
Uuid::from_bytes(arg.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_ctype(self) -> TCUuid {
|
||||||
|
TCUuid(*self.as_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new, randomly-generated UUID.
|
/// Create a new, randomly-generated UUID.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_uuid_new_v4() -> TCUuid {
|
pub extern "C" fn tc_uuid_new_v4() -> TCUuid {
|
||||||
Uuid::new_v4().into()
|
Uuid::new_v4().return_val()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new UUID with the nil value.
|
/// Create a new UUID with the nil value.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_uuid_nil() -> TCUuid {
|
pub extern "C" fn tc_uuid_nil() -> TCUuid {
|
||||||
Uuid::nil().into()
|
Uuid::nil().return_val()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: this must be a simple constant so that cbindgen can evaluate it
|
// NOTE: this must be a simple constant so that cbindgen can evaluate it
|
||||||
|
@ -40,7 +43,7 @@ pub const TC_UUID_STRING_BYTES: usize = 36;
|
||||||
/// Write the string representation of a TCUuid into the given buffer, which must be
|
/// Write the string representation of a TCUuid into the given buffer, which must be
|
||||||
/// at least TC_UUID_STRING_BYTES long. No NUL terminator is added.
|
/// at least TC_UUID_STRING_BYTES long. No NUL terminator is added.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_uuid_to_buf<'a>(uuid: TCUuid, buf: *mut libc::c_char) {
|
pub extern "C" fn tc_uuid_to_buf<'a>(tcuuid: TCUuid, buf: *mut libc::c_char) {
|
||||||
debug_assert!(!buf.is_null());
|
debug_assert!(!buf.is_null());
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - buf is valid for len bytes (by C convention)
|
// - buf is valid for len bytes (by C convention)
|
||||||
|
@ -51,34 +54,34 @@ pub extern "C" fn tc_uuid_to_buf<'a>(uuid: TCUuid, buf: *mut libc::c_char) {
|
||||||
let buf: &'a mut [u8] = unsafe {
|
let buf: &'a mut [u8] = unsafe {
|
||||||
std::slice::from_raw_parts_mut(buf as *mut u8, ::uuid::adapter::Hyphenated::LENGTH)
|
std::slice::from_raw_parts_mut(buf as *mut u8, ::uuid::adapter::Hyphenated::LENGTH)
|
||||||
};
|
};
|
||||||
let uuid: Uuid = uuid.into();
|
let uuid: Uuid = Uuid::from_arg(tcuuid);
|
||||||
uuid.to_hyphenated().encode_lower(buf);
|
uuid.to_hyphenated().encode_lower(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the string representation of a TCUuid into the given buffer, which must be
|
/// Write the string representation of a TCUuid into the given buffer, which must be
|
||||||
/// at least TC_UUID_STRING_BYTES long. No NUL terminator is added.
|
/// at least TC_UUID_STRING_BYTES long. No NUL terminator is added.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_uuid_to_str(uuid: TCUuid) -> *mut TCString<'static> {
|
pub extern "C" fn tc_uuid_to_str(tcuuid: TCUuid) -> *mut TCString<'static> {
|
||||||
let uuid: Uuid = uuid.into();
|
let uuid: Uuid = Uuid::from_arg(tcuuid);
|
||||||
let s = uuid.to_string();
|
let s = uuid.to_string();
|
||||||
TCString::from(s).return_val()
|
// SAFETY: see TCString docstring
|
||||||
|
unsafe { TCString::from(s).return_val() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the given string as a UUID. The string must not be NULL. Returns false on failure.
|
/// Parse the given string as a UUID. Returns false on failure.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_uuid_from_str<'a>(s: *mut TCString, uuid_out: *mut TCUuid) -> bool {
|
pub extern "C" fn tc_uuid_from_str<'a>(s: *mut TCString, uuid_out: *mut TCUuid) -> bool {
|
||||||
|
// TODO: TCResult instead
|
||||||
debug_assert!(!s.is_null());
|
debug_assert!(!s.is_null());
|
||||||
debug_assert!(!uuid_out.is_null());
|
debug_assert!(!uuid_out.is_null());
|
||||||
// SAFETY:
|
// SAFETY: see TCString docstring
|
||||||
// - tcstring is not NULL (promised by caller)
|
let s = unsafe { TCString::take_from_arg(s) };
|
||||||
// - caller is exclusive owner of tcstring (implicitly promised by caller)
|
|
||||||
let s = unsafe { TCString::from_arg(s) };
|
|
||||||
if let Ok(s) = s.as_str() {
|
if let Ok(s) = s.as_str() {
|
||||||
if let Ok(u) = Uuid::parse_str(s) {
|
if let Ok(u) = Uuid::parse_str(s) {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - uuid_out is not NULL (promised by caller)
|
// - uuid_out is not NULL (promised by caller)
|
||||||
// - alignment is not required
|
// - alignment is not required
|
||||||
unsafe { *uuid_out = u.into() };
|
unsafe { u.to_arg_out(uuid_out) };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,30 +41,59 @@ typedef enum TCStatus {
|
||||||
* A replica represents an instance of a user's task data, providing an easy interface
|
* A replica represents an instance of a user's task data, providing an easy interface
|
||||||
* for querying and modifying that data.
|
* for querying and modifying that data.
|
||||||
*
|
*
|
||||||
* TCReplicas are not threadsafe.
|
* # Error Handling
|
||||||
*
|
*
|
||||||
* When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then
|
* When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then
|
||||||
* `tc_replica_error` will return the error message.
|
* `tc_replica_error` will return the error message.
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The `*TCReplica` returned from `tc_replica_new…` functions is owned by the caller and
|
||||||
|
* must later be freed to avoid a memory leak.
|
||||||
|
*
|
||||||
|
* Any function taking a `*TCReplica` requires:
|
||||||
|
* - the pointer must not be NUL;
|
||||||
|
* - the pointer must be one previously returned from a tc_… function;
|
||||||
|
* - the memory referenced by the pointer must never be modified by C code; and
|
||||||
|
* - except for `tc_replica_free`, ownership of a `*TCReplica` remains with the caller.
|
||||||
|
*
|
||||||
|
* Once passed to `tc_replica_free`, a `*TCReplica` becmes invalid and must not be used again.
|
||||||
|
*
|
||||||
|
* TCReplicas are not threadsafe.
|
||||||
*/
|
*/
|
||||||
typedef struct TCReplica TCReplica;
|
typedef struct TCReplica TCReplica;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TCString supports passing strings into and out of the TaskChampion API. A string can contain
|
* TCString supports passing strings into and out of the TaskChampion API.
|
||||||
* embedded NUL characters. Strings containing such embedded NULs cannot be represented as a "C
|
|
||||||
* string" and must be accessed using `tc_string_content_and_len` and `tc_string_clone_with_len`.
|
|
||||||
* In general, these two functions should be used for handling arbitrary data, while more
|
|
||||||
* convenient forms may be used where embedded NUL characters are impossible, such as in static
|
|
||||||
* strings.
|
|
||||||
*
|
*
|
||||||
* Rust expects all strings to be UTF-8, and API functions will fail if given a TCString
|
* # Rust Strings and C Strings
|
||||||
* containing invalid UTF-8.
|
|
||||||
*
|
*
|
||||||
* Unless specified otherwise, functions in this API take ownership of a TCString when it is given
|
* A Rust string can contain embedded NUL characters, while C considers such a character to mark
|
||||||
* as a function argument, and free the string before returning. Callers must not use or free
|
* the end of a string. Strings containing embedded NULs cannot be represented as a "C string"
|
||||||
* strings after passing them to such API functions.
|
* and must be accessed using `tc_string_content_and_len` and `tc_string_clone_with_len`. In
|
||||||
|
* general, these two functions should be used for handling arbitrary data, while more convenient
|
||||||
|
* forms may be used where embedded NUL characters are impossible, such as in static strings.
|
||||||
*
|
*
|
||||||
* When a TCString appears as a return value or output argument, it is the responsibility of the
|
* # UTF-8
|
||||||
* caller to free the string.
|
*
|
||||||
|
* TaskChampion expects all strings to be valid UTF-8. `tc_string_…` functions will fail if given
|
||||||
|
* a `*TCString` containing invalid UTF-8.
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* When a `*TCString` appears as a return value or output argument, ownership is passed to the
|
||||||
|
* caller. The caller must pass that ownerhsip back to another function or free the string.
|
||||||
|
*
|
||||||
|
* Any function taking a `*TCReplica` requires:
|
||||||
|
* - the pointer must not be NUL;
|
||||||
|
* - the pointer must be one previously returned from a tc_… function; and
|
||||||
|
* - the memory referenced by the pointer must never be modified by C code.
|
||||||
|
*
|
||||||
|
* Unless specified otherwise, TaskChampion functions take ownership of a `*TCString` when it is
|
||||||
|
* given as a function argument, and the pointer is invalid when the function returns. Callers
|
||||||
|
* must not use or free TCStrings after passing them to such API functions.
|
||||||
|
*
|
||||||
|
* TCStrings are not threadsafe.
|
||||||
*/
|
*/
|
||||||
typedef struct TCString TCString;
|
typedef struct TCString TCString;
|
||||||
|
|
||||||
|
@ -138,9 +167,8 @@ void tc_tags_free(struct TCTags *tctags);
|
||||||
struct TCReplica *tc_replica_new_in_memory(void);
|
struct TCReplica *tc_replica_new_in_memory(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new TCReplica with an on-disk database having the given filename. The filename must
|
* Create a new TCReplica with an on-disk database having the given filename. On error, a string
|
||||||
* not be NULL. On error, a string is written to the `error_out` parameter (if it is not NULL) and
|
* is written to the `error_out` parameter (if it is not NULL) and NULL is returned.
|
||||||
* NULL is returned.
|
|
||||||
*/
|
*/
|
||||||
struct TCReplica *tc_replica_new_on_disk(struct TCString *path, struct TCString **error_out);
|
struct TCReplica *tc_replica_new_on_disk(struct TCString *path, struct TCString **error_out);
|
||||||
|
|
||||||
|
@ -150,13 +178,11 @@ struct TCReplica *tc_replica_new_on_disk(struct TCString *path, struct TCString
|
||||||
* Returns NULL when the task does not exist, and on error. Consult tc_replica_error
|
* Returns NULL when the task does not exist, and on error. Consult tc_replica_error
|
||||||
* to distinguish the two conditions.
|
* to distinguish the two conditions.
|
||||||
*/
|
*/
|
||||||
struct TCTask *tc_replica_get_task(struct TCReplica *rep, struct TCUuid uuid);
|
struct TCTask *tc_replica_get_task(struct TCReplica *rep, struct TCUuid tcuuid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new task. The task must not already exist.
|
* Create a new task. The task must not already exist.
|
||||||
*
|
*
|
||||||
* The description must not be NULL.
|
|
||||||
*
|
|
||||||
* Returns the task, or NULL on error.
|
* Returns the task, or NULL on error.
|
||||||
*/
|
*/
|
||||||
struct TCTask *tc_replica_new_task(struct TCReplica *rep,
|
struct TCTask *tc_replica_new_task(struct TCReplica *rep,
|
||||||
|
@ -168,7 +194,7 @@ struct TCTask *tc_replica_new_task(struct TCReplica *rep,
|
||||||
*
|
*
|
||||||
* Returns the task, or NULL on error.
|
* Returns the task, or NULL on error.
|
||||||
*/
|
*/
|
||||||
struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid uuid);
|
struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undo local operations until the most recent UndoPoint.
|
* Undo local operations until the most recent UndoPoint.
|
||||||
|
@ -239,7 +265,8 @@ const char *tc_string_content(struct TCString *tcstring);
|
||||||
/**
|
/**
|
||||||
* Get the content of the string as a pointer and length. The given string must not be NULL.
|
* Get the content of the string as a pointer and length. The given string must not be NULL.
|
||||||
* This function can return any string, even one including NUL bytes or invalid UTF-8. The
|
* This function can return any string, even one including NUL bytes or invalid UTF-8. The
|
||||||
* returned buffer is valid until the TCString is freed or passed to another TC API function.
|
* returned buffer is valid until the TCString is freed or passed to another TaskChampio
|
||||||
|
* function.
|
||||||
*
|
*
|
||||||
* This function does _not_ take ownership of the TCString.
|
* This function does _not_ take ownership of the TCString.
|
||||||
*/
|
*/
|
||||||
|
@ -323,7 +350,7 @@ bool tc_task_is_active(struct TCTask *task);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a task has the given tag. If the tag is invalid, this function will simply return
|
* Check if a task has the given tag. If the tag is invalid, this function will simply return
|
||||||
* false with no error from `tc_task_error`. The given tag must not be NULL.
|
* false, with no error from `tc_task_error`.
|
||||||
*/
|
*/
|
||||||
bool tc_task_has_tag(struct TCTask *task, struct TCString *tag);
|
bool tc_task_has_tag(struct TCTask *task, struct TCString *tag);
|
||||||
|
|
||||||
|
@ -417,16 +444,16 @@ struct TCUuid tc_uuid_nil(void);
|
||||||
* Write the string representation of a TCUuid into the given buffer, which must be
|
* Write the string representation of a TCUuid into the given buffer, which must be
|
||||||
* at least TC_UUID_STRING_BYTES long. No NUL terminator is added.
|
* at least TC_UUID_STRING_BYTES long. No NUL terminator is added.
|
||||||
*/
|
*/
|
||||||
void tc_uuid_to_buf(struct TCUuid uuid, char *buf);
|
void tc_uuid_to_buf(struct TCUuid tcuuid, char *buf);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the string representation of a TCUuid into the given buffer, which must be
|
* Write the string representation of a TCUuid into the given buffer, which must be
|
||||||
* at least TC_UUID_STRING_BYTES long. No NUL terminator is added.
|
* at least TC_UUID_STRING_BYTES long. No NUL terminator is added.
|
||||||
*/
|
*/
|
||||||
struct TCString *tc_uuid_to_str(struct TCUuid uuid);
|
struct TCString *tc_uuid_to_str(struct TCUuid tcuuid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the given string as a UUID. The string must not be NULL. Returns false on failure.
|
* Parse the given string as a UUID. Returns false on failure.
|
||||||
*/
|
*/
|
||||||
bool tc_uuid_from_str(struct TCString *s, struct TCUuid *uuid_out);
|
bool tc_uuid_from_str(struct TCString *s, struct TCUuid *uuid_out);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue