mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
implement lists in the same files as singular data
This commit is contained in:
parent
7996a98908
commit
af51e0382a
7 changed files with 208 additions and 226 deletions
|
@ -11,21 +11,15 @@ pub mod replica;
|
|||
pub mod result;
|
||||
pub mod status;
|
||||
pub mod string;
|
||||
pub mod stringlist;
|
||||
pub mod task;
|
||||
pub mod tasklist;
|
||||
pub mod uuid;
|
||||
pub mod uuidlist;
|
||||
|
||||
pub(crate) mod types {
|
||||
pub(crate) use crate::annotation::{TCAnnotation, TCAnnotationList};
|
||||
pub(crate) use crate::replica::TCReplica;
|
||||
pub(crate) use crate::result::TCResult;
|
||||
pub(crate) use crate::status::TCStatus;
|
||||
pub(crate) use crate::string::TCString;
|
||||
pub(crate) use crate::stringlist::TCStringList;
|
||||
pub(crate) use crate::task::TCTask;
|
||||
pub(crate) use crate::tasklist::TCTaskList;
|
||||
pub(crate) use crate::uuid::TCUuid;
|
||||
pub(crate) use crate::uuidlist::TCUuidList;
|
||||
pub(crate) use crate::string::{TCString, TCStringList};
|
||||
pub(crate) use crate::task::{TCTask, TCTaskList};
|
||||
pub(crate) use crate::uuid::{TCUuid, TCUuidList};
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::traits::*;
|
|||
use std::ffi::{CStr, CString, OsStr};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::PathBuf;
|
||||
use std::ptr::NonNull;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
/// TCString supports passing strings into and out of the TaskChampion API.
|
||||
|
@ -136,6 +137,39 @@ impl<'a> From<&str> for TCString<'static> {
|
|||
}
|
||||
}
|
||||
|
||||
/// TCStringList represents a list of strings.
|
||||
///
|
||||
/// The content of this struct must be treated as read-only.
|
||||
#[repr(C)]
|
||||
pub struct TCStringList {
|
||||
/// number of strings in items
|
||||
len: libc::size_t,
|
||||
|
||||
/// total size of items (internal use only)
|
||||
_capacity: libc::size_t,
|
||||
|
||||
/// TCStringList representing each string. these remain owned by the TCStringList instance and will
|
||||
/// be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the
|
||||
/// *TCStringList at indexes 0..len-1 are not NULL.
|
||||
items: *const NonNull<TCString<'static>>,
|
||||
}
|
||||
|
||||
impl CArray for TCStringList {
|
||||
type Element = NonNull<TCString<'static>>;
|
||||
|
||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
||||
TCStringList {
|
||||
len,
|
||||
_capacity: cap,
|
||||
items,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
||||
(self.items, self.len, self._capacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new TCString referencing the given C string. The C string must remain valid and
|
||||
/// unchanged until after the TCString is freed. It's typically easiest to ensure this by using a
|
||||
/// static string.
|
||||
|
@ -281,11 +315,42 @@ pub unsafe extern "C" fn tc_string_free(tcstring: *mut TCString) {
|
|||
drop(unsafe { TCString::take_from_arg(tcstring) });
|
||||
}
|
||||
|
||||
/// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used after
|
||||
/// this call.
|
||||
///
|
||||
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn tc_string_list_free(tcstrings: *mut TCStringList) {
|
||||
debug_assert!(!tcstrings.is_null());
|
||||
// SAFETY:
|
||||
// - *tcstrings is a valid TCStringList (caller promises to treat it as read-only)
|
||||
let strings = unsafe { TCStringList::take_from_arg(tcstrings, TCStringList::null_value()) };
|
||||
TCStringList::drop_vector(strings);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn empty_array_has_non_null_pointer() {
|
||||
let tcstrings = TCStringList::return_val(Vec::new());
|
||||
assert!(!tcstrings.items.is_null());
|
||||
assert_eq!(tcstrings.len, 0);
|
||||
assert_eq!(tcstrings._capacity, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_sets_null_pointer() {
|
||||
let mut tcstrings = TCStringList::return_val(Vec::new());
|
||||
// SAFETY: testing expected behavior
|
||||
unsafe { tc_string_list_free(&mut tcstrings) };
|
||||
assert!(tcstrings.items.is_null());
|
||||
assert_eq!(tcstrings.len, 0);
|
||||
assert_eq!(tcstrings._capacity, 0);
|
||||
}
|
||||
|
||||
const INVALID_UTF8: &[u8] = b"abc\xf0\x28\x8c\x28";
|
||||
|
||||
fn make_cstring() -> TCString<'static> {
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
use crate::traits::*;
|
||||
use crate::types::*;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// TCStringList represents a list of strings.
|
||||
///
|
||||
/// The content of this struct must be treated as read-only.
|
||||
#[repr(C)]
|
||||
pub struct TCStringList {
|
||||
/// number of strings in items
|
||||
len: libc::size_t,
|
||||
|
||||
/// total size of items (internal use only)
|
||||
_capacity: libc::size_t,
|
||||
|
||||
/// TCStringList representing each string. these remain owned by the TCStringList instance and will
|
||||
/// be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the
|
||||
/// *TCStringList at indexes 0..len-1 are not NULL.
|
||||
items: *const NonNull<TCString<'static>>,
|
||||
}
|
||||
|
||||
impl CArray for TCStringList {
|
||||
type Element = NonNull<TCString<'static>>;
|
||||
|
||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
||||
TCStringList {
|
||||
len,
|
||||
_capacity: cap,
|
||||
items,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
||||
(self.items, self.len, self._capacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used after
|
||||
/// this call.
|
||||
///
|
||||
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn tc_string_list_free(tcstrings: *mut TCStringList) {
|
||||
debug_assert!(!tcstrings.is_null());
|
||||
// SAFETY:
|
||||
// - *tcstrings is a valid TCStringList (caller promises to treat it as read-only)
|
||||
let strings = unsafe { TCStringList::take_from_arg(tcstrings, TCStringList::null_value()) };
|
||||
TCStringList::drop_vector(strings);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_array_has_non_null_pointer() {
|
||||
let tcstrings = TCStringList::return_val(Vec::new());
|
||||
assert!(!tcstrings.items.is_null());
|
||||
assert_eq!(tcstrings.len, 0);
|
||||
assert_eq!(tcstrings._capacity, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_sets_null_pointer() {
|
||||
let mut tcstrings = TCStringList::return_val(Vec::new());
|
||||
// SAFETY: testing expected behavior
|
||||
unsafe { tc_string_list_free(&mut tcstrings) };
|
||||
assert!(tcstrings.items.is_null());
|
||||
assert_eq!(tcstrings.len, 0);
|
||||
assert_eq!(tcstrings._capacity, 0);
|
||||
}
|
||||
}
|
|
@ -171,6 +171,39 @@ fn to_datetime(time: libc::time_t) -> Option<DateTime<Utc>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// TCTaskList represents a list of tasks.
|
||||
///
|
||||
/// The content of this struct must be treated as read-only.
|
||||
#[repr(C)]
|
||||
pub struct TCTaskList {
|
||||
/// number of tasks in items
|
||||
len: libc::size_t,
|
||||
|
||||
/// total size of items (internal use only)
|
||||
_capacity: libc::size_t,
|
||||
|
||||
/// array of pointers representing each task. these remain owned by the TCTaskList instance and
|
||||
/// will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList,
|
||||
/// and the *TCTaskList at indexes 0..len-1 are not NULL.
|
||||
items: *const NonNull<TCTask>,
|
||||
}
|
||||
|
||||
impl CArray for TCTaskList {
|
||||
type Element = NonNull<TCTask>;
|
||||
|
||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
||||
TCTaskList {
|
||||
len,
|
||||
_capacity: cap,
|
||||
items,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
||||
(self.items, self.len, self._capacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an immutable task into a mutable task.
|
||||
///
|
||||
/// The task must not be NULL. It is modified in-place, and becomes mutable.
|
||||
|
@ -553,3 +586,39 @@ pub unsafe extern "C" fn tc_task_free<'a>(task: *mut TCTask) {
|
|||
|
||||
drop(tctask);
|
||||
}
|
||||
|
||||
/// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after
|
||||
/// this call.
|
||||
///
|
||||
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn tc_task_list_free(tctasks: *mut TCTaskList) {
|
||||
debug_assert!(!tctasks.is_null());
|
||||
// SAFETY:
|
||||
// - *tctasks is a valid TCTaskList (caller promises to treat it as read-only)
|
||||
let tasks = unsafe { TCTaskList::take_from_arg(tctasks, TCTaskList::null_value()) };
|
||||
TCTaskList::drop_vector(tasks);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_array_has_non_null_pointer() {
|
||||
let tctasks = TCTaskList::return_val(Vec::new());
|
||||
assert!(!tctasks.items.is_null());
|
||||
assert_eq!(tctasks.len, 0);
|
||||
assert_eq!(tctasks._capacity, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_sets_null_pointer() {
|
||||
let mut tctasks = TCTaskList::return_val(Vec::new());
|
||||
// SAFETY: testing expected behavior
|
||||
unsafe { tc_task_list_free(&mut tctasks) };
|
||||
assert!(tctasks.items.is_null());
|
||||
assert_eq!(tctasks.len, 0);
|
||||
assert_eq!(tctasks._capacity, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
use crate::traits::*;
|
||||
use crate::types::*;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// TCTaskList represents a list of tasks.
|
||||
///
|
||||
/// The content of this struct must be treated as read-only.
|
||||
#[repr(C)]
|
||||
pub struct TCTaskList {
|
||||
/// number of tasks in items
|
||||
len: libc::size_t,
|
||||
|
||||
/// total size of items (internal use only)
|
||||
_capacity: libc::size_t,
|
||||
|
||||
/// array of pointers representing each task. these remain owned by the TCTaskList instance and
|
||||
/// will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList,
|
||||
/// and the *TCTaskList at indexes 0..len-1 are not NULL.
|
||||
items: *const NonNull<TCTask>,
|
||||
}
|
||||
|
||||
impl CArray for TCTaskList {
|
||||
type Element = NonNull<TCTask>;
|
||||
|
||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
||||
TCTaskList {
|
||||
len,
|
||||
_capacity: cap,
|
||||
items,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
||||
(self.items, self.len, self._capacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after
|
||||
/// this call.
|
||||
///
|
||||
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn tc_task_list_free(tctasks: *mut TCTaskList) {
|
||||
debug_assert!(!tctasks.is_null());
|
||||
// SAFETY:
|
||||
// - *tctasks is a valid TCTaskList (caller promises to treat it as read-only)
|
||||
let tasks = unsafe { TCTaskList::take_from_arg(tctasks, TCTaskList::null_value()) };
|
||||
TCTaskList::drop_vector(tasks);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_array_has_non_null_pointer() {
|
||||
let tctasks = TCTaskList::return_val(Vec::new());
|
||||
assert!(!tctasks.items.is_null());
|
||||
assert_eq!(tctasks.len, 0);
|
||||
assert_eq!(tctasks._capacity, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_sets_null_pointer() {
|
||||
let mut tctasks = TCTaskList::return_val(Vec::new());
|
||||
// SAFETY: testing expected behavior
|
||||
unsafe { tc_task_list_free(&mut tctasks) };
|
||||
assert!(tctasks.items.is_null());
|
||||
assert_eq!(tctasks.len, 0);
|
||||
assert_eq!(tctasks._capacity, 0);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,10 @@ use crate::types::*;
|
|||
use libc;
|
||||
use taskchampion::Uuid;
|
||||
|
||||
// NOTE: this must be a simple constant so that cbindgen can evaluate it
|
||||
/// Length, in bytes, of the string representation of a UUID (without NUL terminator)
|
||||
pub const TC_UUID_STRING_BYTES: usize = 36;
|
||||
|
||||
/// TCUuid is used as a task identifier. Uuids do not contain any pointers and need not be freed.
|
||||
/// Uuids are typically treated as opaque, but the bytes are available in big-endian format.
|
||||
///
|
||||
|
@ -36,9 +40,37 @@ pub unsafe extern "C" fn tc_uuid_nil() -> TCUuid {
|
|||
TCUuid::return_val(Uuid::nil())
|
||||
}
|
||||
|
||||
// NOTE: this must be a simple constant so that cbindgen can evaluate it
|
||||
/// Length, in bytes, of the string representation of a UUID (without NUL terminator)
|
||||
pub const TC_UUID_STRING_BYTES: usize = 36;
|
||||
/// TCUuidList represents a list of uuids.
|
||||
///
|
||||
/// The content of this struct must be treated as read-only.
|
||||
#[repr(C)]
|
||||
pub struct TCUuidList {
|
||||
/// number of uuids in items
|
||||
len: libc::size_t,
|
||||
|
||||
/// total size of items (internal use only)
|
||||
_capacity: libc::size_t,
|
||||
|
||||
/// array of uuids. these remain owned by the TCUuidList instance and will be freed by
|
||||
/// tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList.
|
||||
items: *const TCUuid,
|
||||
}
|
||||
|
||||
impl CArray for TCUuidList {
|
||||
type Element = TCUuid;
|
||||
|
||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
||||
TCUuidList {
|
||||
len,
|
||||
_capacity: cap,
|
||||
items,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
||||
(self.items, self.len, self._capacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
@ -91,3 +123,39 @@ pub unsafe extern "C" fn tc_uuid_from_str<'a>(s: *mut TCString, uuid_out: *mut T
|
|||
}
|
||||
TCResult::Error
|
||||
}
|
||||
|
||||
/// Free a TCUuidList instance. The instance, and all TCUuids it contains, must not be used after
|
||||
/// this call.
|
||||
///
|
||||
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUuidList.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn tc_uuid_list_free(tcuuids: *mut TCUuidList) {
|
||||
debug_assert!(!tcuuids.is_null());
|
||||
// SAFETY:
|
||||
// - *tcuuids is a valid TCUuidList (caller promises to treat it as read-only)
|
||||
let uuids = unsafe { TCUuidList::take_from_arg(tcuuids, TCUuidList::null_value()) };
|
||||
TCUuidList::drop_vector(uuids);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_array_has_non_null_pointer() {
|
||||
let tcuuids = TCUuidList::return_val(Vec::new());
|
||||
assert!(!tcuuids.items.is_null());
|
||||
assert_eq!(tcuuids.len, 0);
|
||||
assert_eq!(tcuuids._capacity, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_sets_null_pointer() {
|
||||
let mut tcuuids = TCUuidList::return_val(Vec::new());
|
||||
// SAFETY: testing expected behavior
|
||||
unsafe { tc_uuid_list_free(&mut tcuuids) };
|
||||
assert!(tcuuids.items.is_null());
|
||||
assert_eq!(tcuuids.len, 0);
|
||||
assert_eq!(tcuuids._capacity, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
use crate::traits::*;
|
||||
use crate::types::*;
|
||||
|
||||
/// TCUuidList represents a list of uuids.
|
||||
///
|
||||
/// The content of this struct must be treated as read-only.
|
||||
#[repr(C)]
|
||||
pub struct TCUuidList {
|
||||
/// number of uuids in items
|
||||
len: libc::size_t,
|
||||
|
||||
/// total size of items (internal use only)
|
||||
_capacity: libc::size_t,
|
||||
|
||||
/// array of uuids. these remain owned by the TCUuidList instance and will be freed by
|
||||
/// tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList.
|
||||
items: *const TCUuid,
|
||||
}
|
||||
|
||||
impl CArray for TCUuidList {
|
||||
type Element = TCUuid;
|
||||
|
||||
unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self {
|
||||
TCUuidList {
|
||||
len,
|
||||
_capacity: cap,
|
||||
items,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_raw_parts(self) -> (*const Self::Element, usize, usize) {
|
||||
(self.items, self.len, self._capacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Free a TCUuidList instance. The instance, and all TCUuids it contains, must not be used after
|
||||
/// this call.
|
||||
///
|
||||
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUuidList.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn tc_uuid_list_free(tcuuids: *mut TCUuidList) {
|
||||
debug_assert!(!tcuuids.is_null());
|
||||
// SAFETY:
|
||||
// - *tcuuids is a valid TCUuidList (caller promises to treat it as read-only)
|
||||
let uuids = unsafe { TCUuidList::take_from_arg(tcuuids, TCUuidList::null_value()) };
|
||||
TCUuidList::drop_vector(uuids);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_array_has_non_null_pointer() {
|
||||
let tcuuids = TCUuidList::return_val(Vec::new());
|
||||
assert!(!tcuuids.items.is_null());
|
||||
assert_eq!(tcuuids.len, 0);
|
||||
assert_eq!(tcuuids._capacity, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_sets_null_pointer() {
|
||||
let mut tcuuids = TCUuidList::return_val(Vec::new());
|
||||
// SAFETY: testing expected behavior
|
||||
unsafe { tc_uuid_list_free(&mut tcuuids) };
|
||||
assert!(tcuuids.items.is_null());
|
||||
assert_eq!(tcuuids.len, 0);
|
||||
assert_eq!(tcuuids._capacity, 0);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue