use crate::traits::*;
use crate::types::*;
use crate::util::err_to_ruststring;
use crate::TCKV;
use std::convert::TryFrom;
use std::ops::Deref;
use std::ptr::NonNull;
use std::str::FromStr;
use taskchampion::{utc_timestamp, Annotation, Tag, Task, TaskMut, Uuid};
#[ffizz_header::item]
#[ffizz(order = 1000)]
pub struct TCTask {
inner: Inner,
error: Option<RustString<'static>>,
}
enum Inner {
Immutable(Task),
Mutable(TaskMut<'static>, *mut TCReplica),
Invalid,
}
impl PassByPointer for TCTask {}
impl TCTask {
unsafe fn to_mut(&mut self, tcreplica: *mut TCReplica) {
self.inner = match std::mem::replace(&mut self.inner, Inner::Invalid) {
Inner::Immutable(task) => {
let tcreplica_ref: &mut TCReplica =
unsafe { TCReplica::from_ptr_arg_ref_mut(tcreplica) };
let rep_ref = tcreplica_ref.borrow_mut();
Inner::Mutable(task.into_mut(rep_ref), tcreplica)
}
Inner::Mutable(task, tcreplica) => Inner::Mutable(task, tcreplica),
Inner::Invalid => unreachable!(),
}
}
#[allow(clippy::wrong_self_convention)] fn to_immut(&mut self) {
self.inner = match std::mem::replace(&mut self.inner, Inner::Invalid) {
Inner::Immutable(task) => Inner::Immutable(task),
Inner::Mutable(task, tcreplica) => {
let tcreplica_ref: &mut TCReplica =
unsafe { TCReplica::from_ptr_arg_ref_mut(tcreplica) };
tcreplica_ref.release_borrow();
Inner::Immutable(task.into_immut())
}
Inner::Invalid => unreachable!(),
}
}
}
impl From<Task> for TCTask {
fn from(task: Task) -> TCTask {
TCTask {
inner: Inner::Immutable(task),
error: None,
}
}
}
fn wrap<T, F>(task: *mut TCTask, f: F) -> T
where
F: FnOnce(&Task) -> T,
{
let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
let task: &Task = match &tctask.inner {
Inner::Immutable(t) => t,
Inner::Mutable(t, _) => t.deref(),
Inner::Invalid => unreachable!(),
};
tctask.error = None;
f(task)
}
fn wrap_mut<T, F>(task: *mut TCTask, f: F, err_value: T) -> T
where
F: FnOnce(&mut TaskMut) -> anyhow::Result<T>,
{
let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
let task: &mut TaskMut = match tctask.inner {
Inner::Immutable(_) => panic!("Task is immutable"),
Inner::Mutable(ref mut t, _) => t,
Inner::Invalid => unreachable!(),
};
tctask.error = None;
match f(task) {
Ok(rv) => rv,
Err(e) => {
tctask.error = Some(err_to_ruststring(e));
err_value
}
}
}
impl TryFrom<RustString<'static>> for Tag {
type Error = anyhow::Error;
fn try_from(mut rstring: RustString) -> Result<Tag, anyhow::Error> {
let tagstr = rstring.as_str()?;
Tag::from_str(tagstr)
}
}
#[ffizz_header::item]
#[ffizz(order = 1010)]
#[repr(C)]
pub struct TCTaskList {
len: libc::size_t,
capacity: libc::size_t,
items: *mut Option<NonNull<TCTask>>,
}
impl CList for TCTaskList {
type Element = Option<NonNull<TCTask>>;
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
TCTaskList {
len,
capacity: cap,
items,
}
}
fn slice(&mut self) -> &mut [Self::Element] {
unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
}
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
(self.items, self.len, self.capacity)
}
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_uuid(task: *mut TCTask) -> TCUuid {
wrap(task, |task| {
unsafe { TCUuid::return_val(task.get_uuid()) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_status(task: *mut TCTask) -> TCStatus {
wrap(task, |task| task.get_status().into())
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_taskmap(task: *mut TCTask) -> TCKVList {
wrap(task, |task| {
let vec: Vec<TCKV> = task
.get_taskmap()
.iter()
.map(|(k, v)| {
let key = RustString::from(k.as_ref());
let value = RustString::from(v.as_ref());
TCKV::as_ctype((key, value))
})
.collect();
unsafe { TCKVList::return_val(vec) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_description(task: *mut TCTask) -> TCString {
wrap(task, |task| {
let descr = task.get_description();
unsafe { TCString::return_val(descr.into()) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_value(task: *mut TCTask, property: TCString) -> TCString {
let mut property = unsafe { TCString::val_from_arg(property) };
wrap(task, |task| {
if let Ok(property) = property.as_str() {
let value = task.get_value(property);
if let Some(value) = value {
return unsafe { TCString::return_val(value.into()) };
}
}
TCString::default() })
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_entry(task: *mut TCTask) -> libc::time_t {
wrap(task, |task| libc::time_t::as_ctype(task.get_entry()))
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_wait(task: *mut TCTask) -> libc::time_t {
wrap(task, |task| libc::time_t::as_ctype(task.get_wait()))
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_modified(task: *mut TCTask) -> libc::time_t {
wrap(task, |task| libc::time_t::as_ctype(task.get_modified()))
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_is_waiting(task: *mut TCTask) -> bool {
wrap(task, |task| task.is_waiting())
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_is_active(task: *mut TCTask) -> bool {
wrap(task, |task| task.is_active())
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_is_blocked(task: *mut TCTask) -> bool {
wrap(task, |task| task.is_blocked())
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_is_blocking(task: *mut TCTask) -> bool {
wrap(task, |task| task.is_blocking())
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_has_tag(task: *mut TCTask, tag: TCString) -> bool {
let tcstring = unsafe { TCString::val_from_arg(tag) };
wrap(task, |task| {
if let Ok(tag) = Tag::try_from(tcstring) {
task.has_tag(&tag)
} else {
false
}
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_tags(task: *mut TCTask) -> TCStringList {
wrap(task, |task| {
let vec: Vec<TCString> = task
.get_tags()
.map(|t| {
unsafe { TCString::return_val(t.as_ref().into()) }
})
.collect();
unsafe { TCStringList::return_val(vec) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_annotations(task: *mut TCTask) -> TCAnnotationList {
wrap(task, |task| {
let vec: Vec<TCAnnotation> = task
.get_annotations()
.map(|a| {
let description = RustString::from(a.description);
TCAnnotation::as_ctype((a.entry, description))
})
.collect();
unsafe { TCAnnotationList::return_val(vec) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_uda(
task: *mut TCTask,
ns: TCString,
key: TCString,
) -> TCString {
wrap(task, |task| {
if let Ok(ns) = unsafe { TCString::val_from_arg(ns) }.as_str() {
if let Ok(key) = unsafe { TCString::val_from_arg(key) }.as_str() {
if let Some(value) = task.get_uda(ns, key) {
return unsafe { TCString::return_val(value.into()) };
}
}
}
TCString::default()
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_legacy_uda(task: *mut TCTask, key: TCString) -> TCString {
wrap(task, |task| {
if let Ok(key) = unsafe { TCString::val_from_arg(key) }.as_str() {
if let Some(value) = task.get_legacy_uda(key) {
return unsafe { TCString::return_val(value.into()) };
}
}
TCString::default()
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_udas(task: *mut TCTask) -> TCUdaList {
wrap(task, |task| {
let vec: Vec<TCUda> = task
.get_udas()
.map(|((ns, key), value)| {
unsafe {
TCUda::return_val(Uda {
ns: Some(ns.into()),
key: key.into(),
value: value.into(),
})
}
})
.collect();
unsafe { TCUdaList::return_val(vec) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_legacy_udas(task: *mut TCTask) -> TCUdaList {
wrap(task, |task| {
let vec: Vec<TCUda> = task
.get_legacy_udas()
.map(|(key, value)| {
unsafe {
TCUda::return_val(Uda {
ns: None,
key: key.into(),
value: value.into(),
})
}
})
.collect();
unsafe { TCUdaList::return_val(vec) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1001)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_dependencies(task: *mut TCTask) -> TCUuidList {
wrap(task, |task| {
let vec: Vec<TCUuid> = task
.get_dependencies()
.map(|u| {
unsafe { TCUuid::return_val(u) }
})
.collect();
unsafe { TCUuidList::return_val(vec) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1002)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_to_mut(task: *mut TCTask, tcreplica: *mut TCReplica) {
let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
unsafe { tctask.to_mut(tcreplica) };
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_set_status(task: *mut TCTask, status: TCStatus) -> TCResult {
wrap_mut(
task,
|task| {
task.set_status(status.into())?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_set_value(
task: *mut TCTask,
property: TCString,
value: TCString,
) -> TCResult {
let mut property = unsafe { TCString::val_from_arg(property) };
let value = if value.is_null() {
None
} else {
Some(unsafe { TCString::val_from_arg(value) })
};
wrap_mut(
task,
|task| {
let value_str = if let Some(mut v) = value {
Some(v.as_str()?.to_string())
} else {
None
};
task.set_value(property.as_str()?.to_string(), value_str)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_set_description(
task: *mut TCTask,
description: TCString,
) -> TCResult {
let mut description = unsafe { TCString::val_from_arg(description) };
wrap_mut(
task,
|task| {
task.set_description(description.as_str()?.to_string())?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_set_entry(task: *mut TCTask, entry: libc::time_t) -> TCResult {
wrap_mut(
task,
|task| {
task.set_entry(unsafe { entry.from_ctype() })?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_set_wait(task: *mut TCTask, wait: libc::time_t) -> TCResult {
wrap_mut(
task,
|task| {
task.set_wait(unsafe { wait.from_ctype() })?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_set_modified(
task: *mut TCTask,
modified: libc::time_t,
) -> TCResult {
wrap_mut(
task,
|task| {
task.set_modified(
unsafe { modified.from_ctype() }
.ok_or_else(|| anyhow::anyhow!("modified cannot be zero"))?,
)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_start(task: *mut TCTask) -> TCResult {
wrap_mut(
task,
|task| {
task.start()?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_stop(task: *mut TCTask) -> TCResult {
wrap_mut(
task,
|task| {
task.stop()?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_done(task: *mut TCTask) -> TCResult {
wrap_mut(
task,
|task| {
task.done()?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_delete(task: *mut TCTask) -> TCResult {
wrap_mut(
task,
|task| {
task.delete()?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: TCString) -> TCResult {
let tcstring = unsafe { TCString::val_from_arg(tag) };
wrap_mut(
task,
|task| {
let tag = Tag::try_from(tcstring)?;
task.add_tag(&tag)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_remove_tag(task: *mut TCTask, tag: TCString) -> TCResult {
let tcstring = unsafe { TCString::val_from_arg(tag) };
wrap_mut(
task,
|task| {
let tag = Tag::try_from(tcstring)?;
task.remove_tag(&tag)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_add_annotation(
task: *mut TCTask,
annotation: *mut TCAnnotation,
) -> TCResult {
let (entry, description) =
unsafe { TCAnnotation::take_val_from_arg(annotation, TCAnnotation::default()) };
wrap_mut(
task,
|task| {
let description = description.into_string()?;
task.add_annotation(Annotation { entry, description })?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_remove_annotation(task: *mut TCTask, entry: i64) -> TCResult {
wrap_mut(
task,
|task| {
task.remove_annotation(utc_timestamp(entry))?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_set_uda(
task: *mut TCTask,
ns: TCString,
key: TCString,
value: TCString,
) -> TCResult {
let mut ns = unsafe { TCString::val_from_arg(ns) };
let mut key = unsafe { TCString::val_from_arg(key) };
let mut value = unsafe { TCString::val_from_arg(value) };
wrap_mut(
task,
|task| {
task.set_uda(ns.as_str()?, key.as_str()?, value.as_str()?.to_string())?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_remove_uda(
task: *mut TCTask,
ns: TCString,
key: TCString,
) -> TCResult {
let mut ns = unsafe { TCString::val_from_arg(ns) };
let mut key = unsafe { TCString::val_from_arg(key) };
wrap_mut(
task,
|task| {
task.remove_uda(ns.as_str()?, key.as_str()?)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_set_legacy_uda(
task: *mut TCTask,
key: TCString,
value: TCString,
) -> TCResult {
let mut key = unsafe { TCString::val_from_arg(key) };
let mut value = unsafe { TCString::val_from_arg(value) };
wrap_mut(
task,
|task| {
task.set_legacy_uda(key.as_str()?.to_string(), value.as_str()?.to_string())?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_remove_legacy_uda(task: *mut TCTask, key: TCString) -> TCResult {
let mut key = unsafe { TCString::val_from_arg(key) };
wrap_mut(
task,
|task| {
task.remove_legacy_uda(key.as_str()?.to_string())?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_add_dependency(task: *mut TCTask, dep: TCUuid) -> TCResult {
let dep: Uuid = unsafe { TCUuid::val_from_arg(dep) };
wrap_mut(
task,
|task| {
task.add_dependency(dep)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_remove_dependency(task: *mut TCTask, dep: TCUuid) -> TCResult {
let dep: Uuid = unsafe { TCUuid::val_from_arg(dep) };
wrap_mut(
task,
|task| {
task.remove_dependency(dep)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 1004)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_to_immut(task: *mut TCTask) {
let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
tctask.to_immut();
}
#[ffizz_header::item]
#[ffizz(order = 1005)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_error(task: *mut TCTask) -> TCString {
let task: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
if let Some(rstring) = task.error.take() {
unsafe { TCString::return_val(rstring) }
} else {
TCString::default()
}
}
#[ffizz_header::item]
#[ffizz(order = 1006)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_free(task: *mut TCTask) {
let mut tctask = unsafe { TCTask::take_from_ptr_arg(task) };
tctask.to_immut();
drop(tctask);
}
#[ffizz_header::item]
#[ffizz(order = 1011)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_list_take(tasks: *mut TCTaskList, index: usize) -> *mut TCTask {
let p = unsafe { take_optional_pointer_list_item(tasks, index) };
if let Some(p) = p {
p.as_ptr()
} else {
std::ptr::null_mut()
}
}
#[ffizz_header::item]
#[ffizz(order = 1011)]
#[no_mangle]
pub unsafe extern "C" fn tc_task_list_free(tasks: *mut TCTaskList) {
unsafe { drop_optional_pointer_list(tasks) };
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn empty_list_has_non_null_pointer() {
let tasks = unsafe { TCTaskList::return_val(Vec::new()) };
assert!(!tasks.items.is_null());
assert_eq!(tasks.len, 0);
assert_eq!(tasks.capacity, 0);
}
#[test]
fn free_sets_null_pointer() {
let mut tasks = unsafe { TCTaskList::return_val(Vec::new()) };
unsafe { tc_task_list_free(&mut tasks) };
assert!(tasks.items.is_null());
assert_eq!(tasks.len, 0);
assert_eq!(tasks.capacity, 0);
}
}