use crate::traits::*;
use crate::types::*;
use crate::util::err_to_ruststring;
use std::ptr::NonNull;
use taskchampion::storage::ReplicaOp;
use taskchampion::{Replica, StorageConfig};
#[ffizz_header::item]
#[ffizz(order = 900)]
pub struct TCReplica {
inner: Replica,
mut_borrowed: bool,
error: Option<RustString<'static>>,
}
impl PassByPointer for TCReplica {}
impl TCReplica {
pub(crate) fn borrow_mut(&mut self) -> &mut Replica {
if self.mut_borrowed {
panic!("replica is already borrowed");
}
self.mut_borrowed = true;
&mut self.inner
}
pub(crate) fn release_borrow(&mut self) {
if !self.mut_borrowed {
panic!("replica is not borrowed");
}
self.mut_borrowed = false;
}
}
impl From<Replica> for TCReplica {
fn from(rep: Replica) -> TCReplica {
TCReplica {
inner: rep,
mut_borrowed: false,
error: None,
}
}
}
fn wrap<T, F>(rep: *mut TCReplica, f: F, err_value: T) -> T
where
F: FnOnce(&mut Replica) -> anyhow::Result<T>,
{
debug_assert!(!rep.is_null());
let rep: &mut TCReplica = unsafe { TCReplica::from_ptr_arg_ref_mut(rep) };
if rep.mut_borrowed {
panic!("replica is borrowed and cannot be used");
}
rep.error = None;
match f(&mut rep.inner) {
Ok(v) => v,
Err(e) => {
rep.error = Some(err_to_ruststring(e));
err_value
}
}
}
fn wrap_constructor<T, F>(f: F, error_out: *mut TCString, err_value: T) -> T
where
F: FnOnce() -> anyhow::Result<T>,
{
if !error_out.is_null() {
unsafe { *error_out = TCString::default() };
}
match f() {
Ok(v) => v,
Err(e) => {
if !error_out.is_null() {
unsafe {
TCString::val_to_arg_out(err_to_ruststring(e), error_out);
}
}
err_value
}
}
}
#[ffizz_header::item]
#[ffizz(order = 900)]
#[derive(Debug, Default)]
#[repr(u32)]
pub enum TCReplicaOpType {
Create = 0,
Delete = 1,
Update = 2,
UndoPoint = 3,
#[default]
Error = 4,
}
#[ffizz_header::item]
#[ffizz(order = 901)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_new_in_memory() -> *mut TCReplica {
let storage = StorageConfig::InMemory
.into_storage()
.expect("in-memory always succeeds");
unsafe { TCReplica::from(Replica::new(storage)).return_ptr() }
}
#[ffizz_header::item]
#[ffizz(order = 901)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_new_on_disk(
path: TCString,
create_if_missing: bool,
error_out: *mut TCString,
) -> *mut TCReplica {
wrap_constructor(
|| {
let mut path = unsafe { TCString::val_from_arg(path) };
let storage = StorageConfig::OnDisk {
taskdb_dir: path.to_path_buf_mut()?,
create_if_missing,
}
.into_storage()?;
Ok(unsafe { TCReplica::from(Replica::new(storage)).return_ptr() })
},
error_out,
std::ptr::null_mut(),
)
}
#[ffizz_header::item]
#[ffizz(order = 901)]
#[derive(Debug)]
#[repr(C)]
pub struct TCReplicaOp {
operation_type: TCReplicaOpType,
inner: Box<ReplicaOp>,
}
impl From<ReplicaOp> for TCReplicaOp {
fn from(replica_op: ReplicaOp) -> TCReplicaOp {
match replica_op {
ReplicaOp::Create { .. } => TCReplicaOp {
operation_type: TCReplicaOpType::Create,
inner: Box::new(replica_op),
},
ReplicaOp::Delete { .. } => TCReplicaOp {
operation_type: TCReplicaOpType::Delete,
inner: Box::new(replica_op),
},
ReplicaOp::Update { .. } => TCReplicaOp {
operation_type: TCReplicaOpType::Update,
inner: Box::new(replica_op),
},
ReplicaOp::UndoPoint => TCReplicaOp {
operation_type: TCReplicaOpType::UndoPoint,
inner: Box::new(replica_op),
},
}
}
}
#[ffizz_header::item]
#[ffizz(order = 901)]
#[repr(C)]
#[derive(Debug)]
pub struct TCReplicaOpList {
items: *mut TCReplicaOp,
len: usize,
capacity: usize,
}
impl Default for TCReplicaOpList {
fn default() -> Self {
unsafe { TCReplicaOpList::return_val(Vec::new()) }
}
}
impl CList for TCReplicaOpList {
type Element = TCReplicaOp;
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
TCReplicaOpList {
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 = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_all_tasks(rep: *mut TCReplica) -> TCTaskList {
wrap(
rep,
|rep| {
let tasks: Vec<_> = rep
.all_tasks()?
.drain()
.map(|(_uuid, t)| {
Some(
NonNull::new(
unsafe { TCTask::from(t).return_ptr() },
)
.expect("TCTask::return_ptr returned NULL"),
)
})
.collect();
Ok(unsafe { TCTaskList::return_val(tasks) })
},
TCTaskList::null_value(),
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_all_task_uuids(rep: *mut TCReplica) -> TCUuidList {
wrap(
rep,
|rep| {
let uuids: Vec<_> = rep
.all_task_uuids()?
.drain(..)
.map(|uuid| unsafe { TCUuid::return_val(uuid) })
.collect();
Ok(unsafe { TCUuidList::return_val(uuids) })
},
TCUuidList::null_value(),
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_working_set(rep: *mut TCReplica) -> *mut TCWorkingSet {
wrap(
rep,
|rep| {
let ws = rep.working_set()?;
Ok(unsafe { TCWorkingSet::return_ptr(ws.into()) })
},
std::ptr::null_mut(),
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_get_task(rep: *mut TCReplica, tcuuid: TCUuid) -> *mut TCTask {
wrap(
rep,
|rep| {
let uuid = unsafe { TCUuid::val_from_arg(tcuuid) };
if let Some(task) = rep.get_task(uuid)? {
Ok(unsafe { TCTask::from(task).return_ptr() })
} else {
Ok(std::ptr::null_mut())
}
},
std::ptr::null_mut(),
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_new_task(
rep: *mut TCReplica,
status: TCStatus,
description: TCString,
) -> *mut TCTask {
let mut description = unsafe { TCString::val_from_arg(description) };
wrap(
rep,
|rep| {
let task = rep.new_task(status.into(), description.as_str()?.to_string())?;
Ok(unsafe { TCTask::from(task).return_ptr() })
},
std::ptr::null_mut(),
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_import_task_with_uuid(
rep: *mut TCReplica,
tcuuid: TCUuid,
) -> *mut TCTask {
wrap(
rep,
|rep| {
let uuid = unsafe { TCUuid::val_from_arg(tcuuid) };
let task = rep.import_task_with_uuid(uuid)?;
Ok(unsafe { TCTask::from(task).return_ptr() })
},
std::ptr::null_mut(),
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_delete_task(rep: *mut TCReplica, tcuuid: TCUuid) -> TCResult {
wrap(
rep,
|rep| {
let uuid = unsafe { TCUuid::val_from_arg(tcuuid) };
rep.delete_task(uuid)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_sync(
rep: *mut TCReplica,
server: *mut TCServer,
avoid_snapshots: bool,
) -> TCResult {
wrap(
rep,
|rep| {
debug_assert!(!server.is_null());
let server = unsafe { TCServer::from_ptr_arg_ref_mut(server) };
rep.sync(server.as_mut(), avoid_snapshots)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_get_undo_ops(rep: *mut TCReplica) -> TCReplicaOpList {
wrap(
rep,
|rep| {
Ok(unsafe {
TCReplicaOpList::return_val(
rep.get_undo_ops()?
.into_iter()
.map(TCReplicaOp::from)
.collect(),
)
})
},
TCReplicaOpList::default(),
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_commit_undo_ops(
rep: *mut TCReplica,
tc_undo_ops: TCReplicaOpList,
undone_out: *mut i32,
) -> TCResult {
wrap(
rep,
|rep| {
let undo_ops: Vec<ReplicaOp> = unsafe { TCReplicaOpList::val_from_arg(tc_undo_ops) }
.into_iter()
.map(|op| *op.inner)
.collect();
let undone = i32::from(rep.commit_undo_ops(undo_ops)?);
if !undone_out.is_null() {
unsafe { *undone_out = undone };
}
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_num_local_operations(rep: *mut TCReplica) -> i64 {
wrap(
rep,
|rep| {
let count = rep.num_local_operations()? as i64;
Ok(count)
},
-1,
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_num_undo_points(rep: *mut TCReplica) -> i64 {
wrap(
rep,
|rep| {
let count = rep.num_undo_points()? as i64;
Ok(count)
},
-1,
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_add_undo_point(rep: *mut TCReplica, force: bool) -> TCResult {
wrap(
rep,
|rep| {
rep.add_undo_point(force)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_rebuild_working_set(
rep: *mut TCReplica,
renumber: bool,
) -> TCResult {
wrap(
rep,
|rep| {
rep.rebuild_working_set(renumber)?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_error(rep: *mut TCReplica) -> TCString {
let rep: &mut TCReplica = unsafe { TCReplica::from_ptr_arg_ref_mut(rep) };
if let Some(rstring) = rep.error.take() {
unsafe { TCString::return_val(rstring) }
} else {
TCString::default()
}
}
#[ffizz_header::item]
#[ffizz(order = 903)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_free(rep: *mut TCReplica) {
let replica = unsafe { TCReplica::take_from_ptr_arg(rep) };
if replica.mut_borrowed {
panic!("replica is borrowed and cannot be freed");
}
drop(replica);
}
#[ffizz_header::item]
#[ffizz(order = 903)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_op_list_free(oplist: *mut TCReplicaOpList) {
debug_assert!(!oplist.is_null());
unsafe {
TCReplicaOpList::take_val_from_arg(
oplist,
TCReplicaOpList::return_val(Vec::new()),
)
};
}
#[ffizz_header::item]
#[ffizz(order = 903)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_op_get_uuid(op: *const TCReplicaOp) -> TCString {
let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() };
if let ReplicaOp::Create { uuid }
| ReplicaOp::Delete { uuid, .. }
| ReplicaOp::Update { uuid, .. } = rop
{
let uuid_rstr: RustString = uuid.to_string().into();
unsafe { TCString::return_val(uuid_rstr) }
} else {
panic!("Operation has no uuid: {:#?}", rop);
}
}
#[ffizz_header::item]
#[ffizz(order = 903)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_op_get_property(op: *const TCReplicaOp) -> TCString {
let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() };
if let ReplicaOp::Update { property, .. } = rop {
unsafe { TCString::return_val(property.clone().into()) }
} else {
panic!("Operation has no property: {:#?}", rop);
}
}
#[ffizz_header::item]
#[ffizz(order = 903)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_op_get_value(op: *const TCReplicaOp) -> TCString {
let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() };
if let ReplicaOp::Update { value, .. } = rop {
let value_rstr: RustString = value.clone().unwrap_or(String::new()).into();
unsafe { TCString::return_val(value_rstr) }
} else {
panic!("Operation has no value: {:#?}", rop);
}
}
#[ffizz_header::item]
#[ffizz(order = 903)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_op_get_old_value(op: *const TCReplicaOp) -> TCString {
let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() };
if let ReplicaOp::Update { old_value, .. } = rop {
let old_value_rstr: RustString = old_value.clone().unwrap_or(String::new()).into();
unsafe { TCString::return_val(old_value_rstr) }
} else {
panic!("Operation has no old value: {:#?}", rop);
}
}
#[ffizz_header::item]
#[ffizz(order = 903)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_op_get_timestamp(op: *const TCReplicaOp) -> TCString {
let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() };
if let ReplicaOp::Update { timestamp, .. } = rop {
let timestamp_rstr: RustString = timestamp.to_string().into();
unsafe { TCString::return_val(timestamp_rstr) }
} else {
panic!("Operation has no timestamp: {:#?}", rop);
}
}
#[ffizz_header::item]
#[ffizz(order = 903)]
#[no_mangle]
pub unsafe extern "C" fn tc_replica_op_get_old_task_description(
op: *const TCReplicaOp,
) -> TCString {
let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() };
if let ReplicaOp::Delete { old_task, .. } = rop {
let description_rstr: RustString = old_task["description"].clone().into();
unsafe { TCString::return_val(description_rstr) }
} else {
panic!("Operation has no timestamp: {:#?}", rop);
}
}