Use ffizz_header to generate taskchampion.h

This commit is contained in:
Dustin J. Mitchell 2023-01-17 03:23:43 +00:00 committed by Dustin J. Mitchell
parent 989a330e46
commit 75e10676ce
20 changed files with 1881 additions and 1281 deletions

140
Cargo.lock generated
View file

@ -301,17 +301,6 @@ version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -408,25 +397,6 @@ dependencies = [
"bytes", "bytes",
] ]
[[package]]
name = "cbindgen"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb"
dependencies = [
"clap 3.2.22",
"heck",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn",
"tempfile",
"toml",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.73" version = "1.0.73"
@ -458,21 +428,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "clap"
version = "3.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
dependencies = [
"atty",
"bitflags 1.3.2",
"clap_lex 0.2.2",
"indexmap",
"strsim",
"termcolor",
"textwrap",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.3.0" version = "4.3.0"
@ -491,19 +446,10 @@ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"bitflags 1.3.2", "bitflags 1.3.2",
"clap_lex 0.5.0", "clap_lex",
"strsim", "strsim",
] ]
[[package]]
name = "clap_lex"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
dependencies = [
"os_str_bytes",
]
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.5.0" version = "0.5.0"
@ -606,6 +552,12 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.31" version = "0.8.31"
@ -670,6 +622,29 @@ dependencies = [
"instant", "instant",
] ]
[[package]]
name = "ffizz-header"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b3ae8dccc2b5edfc7805a0c26cc776ae521fd5f6fdd693520e130abcdce06"
dependencies = [
"ffizz-macros",
"itertools",
"linkme",
]
[[package]]
name = "ffizz-macros"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56603703fdb7bcae099f7212b9afb83f0d057236e42d87c894ba6b34ad77ac18"
dependencies = [
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.24" version = "1.0.24"
@ -990,6 +965,15 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.2" version = "1.0.2"
@ -1049,6 +1033,26 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "linkme"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfc2b30967da1bcca8f15aa741f2b949a315ef0eabd0ef630a5a0643d7a45260"
dependencies = [
"linkme-impl",
]
[[package]]
name = "linkme-impl"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a440f823b734f5a90d7cc2850a2254611092e88fa13fb1948556858ce2d35d2a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.3.8" version = "0.3.8"
@ -1167,12 +1171,6 @@ version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
[[package]]
name = "os_str_bytes"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
[[package]] [[package]]
name = "output_vt100" name = "output_vt100"
version = "0.1.3" version = "0.1.3"
@ -1673,6 +1671,7 @@ name = "taskchampion-lib"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ffizz-header",
"libc", "libc",
"pretty_assertions", "pretty_assertions",
"taskchampion", "taskchampion",
@ -1686,7 +1685,7 @@ dependencies = [
"actix-web", "actix-web",
"anyhow", "anyhow",
"chrono", "chrono",
"clap 4.3.0", "clap",
"env_logger", "env_logger",
"futures", "futures",
"log", "log",
@ -1722,12 +1721,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "textwrap"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.37" version = "1.0.37"
@ -1831,15 +1824,6 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.34" version = "0.1.34"
@ -2212,7 +2196,7 @@ name = "xtask"
version = "0.4.1" version = "0.4.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cbindgen", "taskchampion-lib",
] ]
[[package]] [[package]]

87
src/tc/rust/Cargo.lock generated
View file

@ -161,6 +161,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]] [[package]]
name = "fallible-iterator" name = "fallible-iterator"
version = "0.2.0" version = "0.2.0"
@ -173,6 +179,29 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "ffizz-header"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b3ae8dccc2b5edfc7805a0c26cc776ae521fd5f6fdd693520e130abcdce06"
dependencies = [
"ffizz-macros",
"itertools",
"linkme",
]
[[package]]
name = "ffizz-macros"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56603703fdb7bcae099f7212b9afb83f0d057236e42d87c894ba6b34ad77ac18"
dependencies = [
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.22" version = "1.0.22"
@ -265,6 +294,15 @@ dependencies = [
"unicode-normalization", "unicode-normalization",
] ]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.1" version = "1.0.1"
@ -312,6 +350,26 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "linkme"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22bcb06ef182e7557cf18d85bd151319d657bd8f699d381435781871f3027af8"
dependencies = [
"linkme-impl",
]
[[package]]
name = "linkme-impl"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83f2011c1121c45eb4d9639cf5dcbae9622d2978fc5e922a346bfdc6c46700b5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.17"
@ -376,18 +434,18 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [ dependencies = [
"unicode-xid", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.17" version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -525,13 +583,13 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.91" version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"unicode-xid", "unicode-ident",
] ]
[[package]] [[package]]
@ -559,6 +617,7 @@ name = "taskchampion-lib"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ffizz-header",
"libc", "libc",
"taskchampion", "taskchampion",
] ]
@ -630,6 +689,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
version = "0.1.19" version = "0.1.19"
@ -645,12 +710,6 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.7.1" version = "0.7.1"

View file

@ -13,9 +13,11 @@ fn build_bindings_tests(suites: &[&'static str]) {
"UNITY_OUTPUT_CHAR_HEADER_DECLARATION", "UNITY_OUTPUT_CHAR_HEADER_DECLARATION",
"test_output(char c)", "test_output(char c)",
); );
build.file("src/bindings_tests/unity/unity.c");
let mut files = vec!["src/bindings_tests/test.c".to_string()]; let mut files = vec![
"src/bindings_tests/test.c".into(),
"src/bindings_tests/unity/unity.c".into(),
];
for suite in suites { for suite in suites {
files.push(format!("src/bindings_tests/{}.c", suite)); files.push(format!("src/bindings_tests/{}.c", suite));
} }
@ -23,6 +25,7 @@ fn build_bindings_tests(suites: &[&'static str]) {
build.file(&file); build.file(&file);
println!("cargo:rerun-if-changed={}", file); println!("cargo:rerun-if-changed={}", file);
} }
println!("cargo:rerun-if-changed=../lib/taskchampion.h");
build.compile("bindings-tests"); build.compile("bindings-tests");
} }

View file

@ -7,6 +7,7 @@ edition = "2018"
libc = "0.2.136" libc = "0.2.136"
taskchampion = { path = "../taskchampion" } taskchampion = { path = "../taskchampion" }
anyhow = "1.0" anyhow = "1.0"
ffizz-header = "0.3"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1" pretty_assertions = "1"

View file

@ -1,76 +0,0 @@
/**
* TaskChampion
*
* This file defines the C interface to libtaskchampion. This is a thin
* wrapper around the Rust `taskchampion` crate. Refer to the documentation
* for that crate at https://docs.rs/taskchampion/latest/taskchampion/ for API
* details. The comments in this file focus mostly on the low-level details of
* passing values to and from TaskChampion.
*
* # Overview
*
* This library defines two major types used to interact with the API, that map directly
* to Rust types.
*
* * TCReplica - see https://docs.rs/taskchampion/latest/taskchampion/struct.Replica.html
* * TCTask - see https://docs.rs/taskchampion/latest/taskchampion/struct.Task.html
* * TCServer - see https://docs.rs/taskchampion/latest/taskchampion/trait.Server.html
* * TCWorkingSet - see https://docs.rs/taskchampion/latest/taskchampion/struct.WorkingSet.html
*
* It also defines a few utility types:
*
* * TCString - a wrapper around both C (NUL-terminated) and Rust (always utf-8) strings.
* * TCList - a list of objects represented as a C array
* * see below for the remainder
*
* # Safety
*
* Each type contains specific instructions to ensure memory safety.
* The general rules are as follows.
*
* No types in this library are threadsafe. All values should be used in only
* one thread for their entire lifetime. It is safe to use unrelated values in
* different threads (for example, different threads may use different
* TCReplica values concurrently).
*
* ## Pass by Pointer
*
* Several types such as TCReplica and TCString are "opaque" types and always
* handled as pointers in C. The bytes these pointers address are private to
* the Rust implemetation and must not be accessed from C.
*
* Pass-by-pointer values have exactly one owner, and that owner is responsible
* for freeing the value (using a `tc__free` function), or transferring
* ownership elsewhere. Except where documented otherwise, when a value is
* passed to C, ownership passes to C as well. When a value is passed to Rust,
* ownership stays with the C code. The exception is TCString, ownership of
* which passes to Rust when it is used as a function argument.
*
* The limited circumstances where one value must not outlive another, due to
* pointer references between them, are documented below.
*
* ## Pass by Value
*
* Types such as TCUuid and TCList are passed by value, and contain fields
* that are accessible from C. C code is free to access the content of these
* types in a _read_only_ fashion.
*
* Pass-by-value values that contain pointers also have exactly one owner,
* responsible for freeing the value or transferring ownership. The tc__free
* functions for these types will replace the pointers with NULL to guard
* against use-after-free errors. The interior pointers in such values should
* never be freed directly (for example, `tc_string_free(tcuda.value)` is an
* error).
*
* TCUuid is a special case, because it does not contain pointers. It can be
* freely copied and need not be freed.
*
* ## Lists
*
* Lists are a special kind of pass-by-value type. Each contains `len` and
* `items`, where `items` is an array of length `len`. Lists, and the values
* in the `items` array, must be treated as read-only. On return from an API
* function, a list's ownership is with the C caller, which must eventually
* free the list. List data must be freed with the `tc__list_free` function.
* It is an error to free any value in the `items` array of a list.
*/

View file

@ -2,6 +2,10 @@ use crate::traits::*;
use crate::types::*; use crate::types::*;
use taskchampion::chrono::prelude::*; use taskchampion::chrono::prelude::*;
#[ffizz_header::item]
#[ffizz(order = 400)]
/// ***** TCAnnotation *****
///
/// TCAnnotation contains the details of an annotation. /// TCAnnotation contains the details of an annotation.
/// ///
/// # Safety /// # Safety
@ -17,6 +21,15 @@ use taskchampion::chrono::prelude::*;
/// after the call returns. In fact, the value will be zeroed out to ensure this. /// after the call returns. In fact, the value will be zeroed out to ensure this.
/// ///
/// TCAnnotations are not threadsafe. /// TCAnnotations are not threadsafe.
///
/// ```c
/// typedef struct TCAnnotation {
/// // Time the annotation was made. Must be nonzero.
/// time_t entry;
/// // Content of the annotation. Must not be NULL.
/// TCString description;
/// } TCAnnotation;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCAnnotation { pub struct TCAnnotation {
/// Time the annotation was made. Must be nonzero. /// Time the annotation was made. Must be nonzero.
@ -62,18 +75,34 @@ impl Default for TCAnnotation {
} }
} }
#[ffizz_header::item]
#[ffizz(order = 410)]
/// ***** TCAnnotationList *****
///
/// TCAnnotationList represents a list of annotations. /// TCAnnotationList represents a list of annotations.
/// ///
/// The content of this struct must be treated as read-only. /// The content of this struct must be treated as read-only.
///
/// ```c
/// typedef struct TCAnnotationList {
/// // number of annotations in items
/// size_t len;
/// // reserved
/// size_t _u1;
/// // Array of annotations. These remain owned by the TCAnnotationList instance and will be freed by
/// // tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList.
/// struct TCAnnotation *items;
/// } TCAnnotationList;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCAnnotationList { pub struct TCAnnotationList {
/// number of annotations in items /// number of annotations in items
len: libc::size_t, len: libc::size_t,
/// total size of items (internal use only) /// total size of items (internal use only)
_capacity: libc::size_t, capacity: libc::size_t,
/// array of annotations. these remain owned by the TCAnnotationList instance and will be freed by /// Array of annotations. These remain owned by the TCAnnotationList instance and will be freed by
/// tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList. /// tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList.
items: *mut TCAnnotation, items: *mut TCAnnotation,
} }
@ -84,7 +113,7 @@ impl CList for TCAnnotationList {
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
TCAnnotationList { TCAnnotationList {
len, len,
_capacity: cap, capacity: cap,
items, items,
} }
} }
@ -99,12 +128,18 @@ impl CList for TCAnnotationList {
} }
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
(self.items, self.len, self._capacity) (self.items, self.len, self.capacity)
} }
} }
#[ffizz_header::item]
#[ffizz(order = 401)]
/// Free a TCAnnotation instance. The instance, and the TCString it contains, must not be used /// Free a TCAnnotation instance. The instance, and the TCString it contains, must not be used
/// after this call. /// after this call.
///
/// ```c
/// EXTERN_C void tc_annotation_free(struct TCAnnotation *tcann);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_annotation_free(tcann: *mut TCAnnotation) { pub unsafe extern "C" fn tc_annotation_free(tcann: *mut TCAnnotation) {
debug_assert!(!tcann.is_null()); debug_assert!(!tcann.is_null());
@ -115,10 +150,16 @@ pub unsafe extern "C" fn tc_annotation_free(tcann: *mut TCAnnotation) {
drop(annotation); drop(annotation);
} }
#[ffizz_header::item]
#[ffizz(order = 411)]
/// Free a TCAnnotationList instance. The instance, and all TCAnnotations it contains, must not be used after /// Free a TCAnnotationList instance. The instance, and all TCAnnotations it contains, must not be used after
/// this call. /// this call.
/// ///
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCAnnotationList. /// When this call returns, the `items` pointer will be NULL, signalling an invalid TCAnnotationList.
///
/// ```c
/// EXTERN_C void tc_annotation_list_free(struct TCAnnotationList *tcanns);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_annotation_list_free(tcanns: *mut TCAnnotationList) { pub unsafe extern "C" fn tc_annotation_list_free(tcanns: *mut TCAnnotationList) {
// SAFETY: // SAFETY:
@ -137,7 +178,7 @@ mod test {
let tcanns = unsafe { TCAnnotationList::return_val(Vec::new()) }; let tcanns = unsafe { TCAnnotationList::return_val(Vec::new()) };
assert!(!tcanns.items.is_null()); assert!(!tcanns.items.is_null());
assert_eq!(tcanns.len, 0); assert_eq!(tcanns.len, 0);
assert_eq!(tcanns._capacity, 0); assert_eq!(tcanns.capacity, 0);
} }
#[test] #[test]
@ -147,6 +188,6 @@ mod test {
unsafe { tc_annotation_list_free(&mut tcanns) }; unsafe { tc_annotation_list_free(&mut tcanns) };
assert!(tcanns.items.is_null()); assert!(tcanns.items.is_null());
assert_eq!(tcanns.len, 0); assert_eq!(tcanns.len, 0);
assert_eq!(tcanns._capacity, 0); assert_eq!(tcanns.capacity, 0);
} }
} }

View file

@ -1,10 +1,21 @@
use crate::traits::*; use crate::traits::*;
use crate::types::*; use crate::types::*;
#[ffizz_header::item]
#[ffizz(order = 600)]
/// ***** TCKV *****
///
/// TCKV contains a key/value pair that is part of a task. /// TCKV contains a key/value pair that is part of a task.
/// ///
/// Neither key nor value are ever NULL. They remain owned by the TCKV and /// Neither key nor value are ever NULL. They remain owned by the TCKV and
/// will be freed when it is freed with tc_kv_list_free. /// will be freed when it is freed with tc_kv_list_free.
///
/// ```c
/// typedef struct TCKV {
/// struct TCString key;
/// struct TCString value;
/// } TCKV;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCKV { pub struct TCKV {
pub key: TCString, pub key: TCString,
@ -37,9 +48,25 @@ impl PassByValue for TCKV {
} }
} }
#[ffizz_header::item]
#[ffizz(order = 610)]
/// ***** TCKVList *****
///
/// TCKVList represents a list of key/value pairs. /// TCKVList represents a list of key/value pairs.
/// ///
/// The content of this struct must be treated as read-only. /// The content of this struct must be treated as read-only.
///
/// ```c
/// typedef struct TCKVList {
/// // number of key/value pairs in items
/// size_t len;
/// // reserved
/// size_t _u1;
/// // Array of TCKV's. These remain owned by the TCKVList instance and will be freed by
/// // tc_kv_list_free. This pointer is never NULL for a valid TCKVList.
/// struct TCKV *items;
/// } TCKVList;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCKVList { pub struct TCKVList {
/// number of key/value pairs in items /// number of key/value pairs in items
@ -48,7 +75,7 @@ pub struct TCKVList {
/// total size of items (internal use only) /// total size of items (internal use only)
_capacity: libc::size_t, _capacity: libc::size_t,
/// array of TCKV's. these remain owned by the TCKVList instance and will be freed by /// Array of TCKV's. These remain owned by the TCKVList instance and will be freed by
/// tc_kv_list_free. This pointer is never NULL for a valid TCKVList. /// tc_kv_list_free. This pointer is never NULL for a valid TCKVList.
items: *mut TCKV, items: *mut TCKV,
} }
@ -78,10 +105,18 @@ impl CList for TCKVList {
} }
} }
// NOTE: callers never have a TCKV that is not in a list, so there is no tc_kv_free.
#[ffizz_header::item]
#[ffizz(order = 611)]
/// Free a TCKVList instance. The instance, and all TCKVs it contains, must not be used after /// Free a TCKVList instance. The instance, and all TCKVs it contains, must not be used after
/// this call. /// this call.
/// ///
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCKVList. /// When this call returns, the `items` pointer will be NULL, signalling an invalid TCKVList.
///
/// ```c
/// EXTERN_C void tc_kv_list_free(struct TCKVList *tckvs);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_kv_list_free(tckvs: *mut TCKVList) { pub unsafe extern "C" fn tc_kv_list_free(tckvs: *mut TCKVList) {
// SAFETY: // SAFETY:

View file

@ -12,6 +12,117 @@
#![deny(clippy::extra_unused_lifetimes)] #![deny(clippy::extra_unused_lifetimes)]
#![deny(clippy::unnecessary_to_owned)] #![deny(clippy::unnecessary_to_owned)]
// ffizz_header orders:
//
// 000-099: header matter
// 100-199: TCResult
// 200-299: TCString / List
// 300-399: TCUuid / List
// 400-499: TCAnnotation / List
// 500-599: TCUda / List
// 600-699: TCKV / List
// 700-799: TCStatus
// 800-899: TCServer
// 900-999: TCReplica
// 1000-1099: TCTask / List
// 1100-1199: TCWorkingSet
// 10000-10099: footer
ffizz_header::snippet! {
#[ffizz(name="intro", order=0)]
/// TaskChampion
///
/// This file defines the C interface to libtaskchampion. This is a thin wrapper around the Rust
/// `taskchampion` crate. Refer to the documentation for that crate at
/// https://docs.rs/taskchampion/latest/taskchampion/ for API details. The comments in this file
/// focus mostly on the low-level details of passing values to and from TaskChampion.
///
/// # Overview
///
/// This library defines two major types used to interact with the API, that map directly to Rust
/// types.
///
/// * TCReplica - see https://docs.rs/taskchampion/latest/taskchampion/struct.Replica.html * TCTask
/// - see https://docs.rs/taskchampion/latest/taskchampion/struct.Task.html * TCServer - see
/// https://docs.rs/taskchampion/latest/taskchampion/trait.Server.html * TCWorkingSet - see
/// https://docs.rs/taskchampion/latest/taskchampion/struct.WorkingSet.html
///
/// It also defines a few utility types:
///
/// * TCString - a wrapper around both C (NUL-terminated) and Rust (always utf-8) strings. *
/// TC…List - a list of objects represented as a C array * see below for the remainder
///
/// # Safety
///
/// Each type contains specific instructions to ensure memory safety. The general rules are as
/// follows.
///
/// No types in this library are threadsafe. All values should be used in only one thread for their
/// entire lifetime. It is safe to use unrelated values in different threads (for example,
/// different threads may use different TCReplica values concurrently).
///
/// ## Pass by Pointer
///
/// Several types such as TCReplica and TCString are "opaque" types and always handled as pointers
/// in C. The bytes these pointers address are private to the Rust implemetation and must not be
/// accessed from C.
///
/// Pass-by-pointer values have exactly one owner, and that owner is responsible for freeing the
/// value (using a `tc_…_free` function), or transferring ownership elsewhere. Except where
/// documented otherwise, when a value is passed to C, ownership passes to C as well. When a value
/// is passed to Rust, ownership stays with the C code. The exception is TCString, ownership of
/// which passes to Rust when it is used as a function argument.
///
/// The limited circumstances where one value must not outlive another, due to pointer references
/// between them, are documented below.
///
/// ## Pass by Value
///
/// Types such as TCUuid and TC…List are passed by value, and contain fields that are accessible
/// from C. C code is free to access the content of these types in a _read_only_ fashion.
///
/// Pass-by-value values that contain pointers also have exactly one owner, responsible for freeing
/// the value or transferring ownership. The tc_…_free functions for these types will replace the
/// pointers with NULL to guard against use-after-free errors. The interior pointers in such values
/// should never be freed directly (for example, `tc_string_free(tcuda.value)` is an error).
///
/// TCUuid is a special case, because it does not contain pointers. It can be freely copied and
/// need not be freed.
///
/// ## Lists
///
/// Lists are a special kind of pass-by-value type. Each contains `len` and `items`, where `items`
/// is an array of length `len`. Lists, and the values in the `items` array, must be treated as
/// read-only. On return from an API function, a list's ownership is with the C caller, which must
/// eventually free the list. List data must be freed with the `tc_…_list_free` function. It is an
/// error to free any value in the `items` array of a list.
}
ffizz_header::snippet! {
#[ffizz(name="topmatter", order=1)]
/// ```c
/// #ifndef TASKCHAMPION_H
/// #define TASKCHAMPION_H
///
/// #include <stdbool.h>
/// #include <stdint.h>
/// #include <time.h>
///
/// #ifdef __cplusplus
/// #define EXTERN_C extern "C"
/// #else
/// #define EXTERN_C
/// #endif // __cplusplus
/// ```
}
ffizz_header::snippet! {
#[ffizz(name="bottomatter", order=10000)]
/// ```c
/// #endif /* TASKCHAMPION_H */
/// ```
}
mod traits; mod traits;
mod util; mod util;
@ -53,3 +164,9 @@ pub(crate) mod types {
pub(crate) use crate::uuid::{TCUuid, TCUuidList}; pub(crate) use crate::uuid::{TCUuid, TCUuidList};
pub(crate) use crate::workingset::TCWorkingSet; pub(crate) use crate::workingset::TCWorkingSet;
} }
#[cfg(debug_assertions)]
/// Generate the taskchapion.h header
pub fn generate_header() -> String {
ffizz_header::generate()
}

View file

@ -4,6 +4,10 @@ use crate::util::err_to_ruststring;
use std::ptr::NonNull; use std::ptr::NonNull;
use taskchampion::{Replica, StorageConfig}; use taskchampion::{Replica, StorageConfig};
#[ffizz_header::item]
#[ffizz(order = 900)]
/// ***** TCReplica *****
///
/// 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.
/// ///
@ -26,6 +30,10 @@ use taskchampion::{Replica, StorageConfig};
/// Once passed to `tc_replica_free`, a `*TCReplica` becomes invalid and must not be used again. /// Once passed to `tc_replica_free`, a `*TCReplica` becomes invalid and must not be used again.
/// ///
/// TCReplicas are not threadsafe. /// TCReplicas are not threadsafe.
///
/// ```c
/// typedef struct TCReplica TCReplica;
/// ```
pub struct TCReplica { pub struct TCReplica {
/// The wrapped Replica /// The wrapped Replica
inner: Replica, inner: Replica,
@ -122,8 +130,14 @@ where
} }
} }
#[ffizz_header::item]
#[ffizz(order = 901)]
/// Create a new TCReplica with an in-memory database. The contents of the database will be /// Create a new TCReplica with an in-memory database. The contents of the database will be
/// lost when it is freed with tc_replica_free. /// lost when it is freed with tc_replica_free.
///
/// ```c
/// EXTERN_C struct TCReplica *tc_replica_new_in_memory(void);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_new_in_memory() -> *mut TCReplica { pub unsafe extern "C" fn tc_replica_new_in_memory() -> *mut TCReplica {
let storage = StorageConfig::InMemory let storage = StorageConfig::InMemory
@ -134,9 +148,17 @@ pub unsafe extern "C" fn tc_replica_new_in_memory() -> *mut TCReplica {
unsafe { TCReplica::from(Replica::new(storage)).return_ptr() } unsafe { TCReplica::from(Replica::new(storage)).return_ptr() }
} }
#[ffizz_header::item]
#[ffizz(order = 901)]
/// Create a new TCReplica with an on-disk database having the given filename. On error, a string /// Create a new TCReplica with an on-disk database having the given filename. On error, a string
/// is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller /// is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller
/// must free this string. /// must free this string.
///
/// ```c
/// EXTERN_C struct TCReplica *tc_replica_new_on_disk(struct TCString path,
/// bool create_if_missing,
/// struct TCString *error_out);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_new_on_disk( pub unsafe extern "C" fn tc_replica_new_on_disk(
path: TCString, path: TCString,
@ -164,9 +186,15 @@ pub unsafe extern "C" fn tc_replica_new_on_disk(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Get a list of all tasks in the replica. /// Get a list of all tasks in the replica.
/// ///
/// Returns a TCTaskList with a NULL items field on error. /// Returns a TCTaskList with a NULL items field on error.
///
/// ```c
/// EXTERN_C struct TCTaskList tc_replica_all_tasks(struct TCReplica *rep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_all_tasks(rep: *mut TCReplica) -> TCTaskList { pub unsafe extern "C" fn tc_replica_all_tasks(rep: *mut TCReplica) -> TCTaskList {
wrap( wrap(
@ -197,11 +225,17 @@ pub unsafe extern "C" fn tc_replica_all_tasks(rep: *mut TCReplica) -> TCTaskList
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Get a list of all uuids for tasks in the replica. /// Get a list of all uuids for tasks in the replica.
/// ///
/// Returns a TCUuidList with a NULL items field on error. /// Returns a TCUuidList with a NULL items field on error.
/// ///
/// The caller must free the UUID list with `tc_uuid_list_free`. /// The caller must free the UUID list with `tc_uuid_list_free`.
///
/// ```c
/// EXTERN_C struct TCUuidList tc_replica_all_task_uuids(struct TCReplica *rep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_all_task_uuids(rep: *mut TCReplica) -> TCUuidList { pub unsafe extern "C" fn tc_replica_all_task_uuids(rep: *mut TCReplica) -> TCUuidList {
wrap( wrap(
@ -222,10 +256,16 @@ pub unsafe extern "C" fn tc_replica_all_task_uuids(rep: *mut TCReplica) -> TCUui
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Get the current working set for this replica. The resulting value must be freed /// Get the current working set for this replica. The resulting value must be freed
/// with tc_working_set_free. /// with tc_working_set_free.
/// ///
/// Returns NULL on error. /// Returns NULL on error.
///
/// ```c
/// EXTERN_C struct TCWorkingSet *tc_replica_working_set(struct TCReplica *rep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_working_set(rep: *mut TCReplica) -> *mut TCWorkingSet { pub unsafe extern "C" fn tc_replica_working_set(rep: *mut TCReplica) -> *mut TCWorkingSet {
wrap( wrap(
@ -240,10 +280,16 @@ pub unsafe extern "C" fn tc_replica_working_set(rep: *mut TCReplica) -> *mut TCW
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Get an existing task by its UUID. /// Get an existing task by its UUID.
/// ///
/// 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.
///
/// ```c
/// EXTERN_C struct TCTask *tc_replica_get_task(struct TCReplica *rep, struct TCUuid tcuuid);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_get_task(rep: *mut TCReplica, tcuuid: TCUuid) -> *mut TCTask { pub unsafe extern "C" fn tc_replica_get_task(rep: *mut TCReplica, tcuuid: TCUuid) -> *mut TCTask {
wrap( wrap(
@ -265,9 +311,17 @@ pub unsafe extern "C" fn tc_replica_get_task(rep: *mut TCReplica, tcuuid: TCUuid
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Create a new task. The task must not already exist. /// Create a new task. The task must not already exist.
/// ///
/// Returns the task, or NULL on error. /// Returns the task, or NULL on error.
///
/// ```c
/// EXTERN_C struct TCTask *tc_replica_new_task(struct TCReplica *rep,
/// enum TCStatus status,
/// struct TCString description);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_new_task( pub unsafe extern "C" fn tc_replica_new_task(
rep: *mut TCReplica, rep: *mut TCReplica,
@ -290,9 +344,15 @@ pub unsafe extern "C" fn tc_replica_new_task(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Create a new task. The task must not already exist. /// Create a new task. The task must not already exist.
/// ///
/// Returns the task, or NULL on error. /// Returns the task, or NULL on error.
///
/// ```c
/// EXTERN_C struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_import_task_with_uuid( pub unsafe extern "C" fn tc_replica_import_task_with_uuid(
rep: *mut TCReplica, rep: *mut TCReplica,
@ -314,9 +374,15 @@ pub unsafe extern "C" fn tc_replica_import_task_with_uuid(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Synchronize this replica with a server. /// Synchronize this replica with a server.
/// ///
/// The `server` argument remains owned by the caller, and must be freed explicitly. /// The `server` argument remains owned by the caller, and must be freed explicitly.
///
/// ```c
/// EXTERN_C TCResult tc_replica_sync(struct TCReplica *rep, struct TCServer *server, bool avoid_snapshots);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_sync( pub unsafe extern "C" fn tc_replica_sync(
rep: *mut TCReplica, rep: *mut TCReplica,
@ -340,10 +406,16 @@ pub unsafe extern "C" fn tc_replica_sync(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Undo local operations until the most recent UndoPoint. /// Undo local operations until the most recent UndoPoint.
/// ///
/// If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if /// If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if
/// there are no operations that can be done. /// there are no operations that can be done.
///
/// ```c
/// EXTERN_C TCResult tc_replica_undo(struct TCReplica *rep, int32_t *undone_out);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_undo(rep: *mut TCReplica, undone_out: *mut i32) -> TCResult { pub unsafe extern "C" fn tc_replica_undo(rep: *mut TCReplica, undone_out: *mut i32) -> TCResult {
wrap( wrap(
@ -362,8 +434,14 @@ pub unsafe extern "C" fn tc_replica_undo(rep: *mut TCReplica, undone_out: *mut i
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Get the number of local, un-synchronized operations (not including undo points), or -1 on /// Get the number of local, un-synchronized operations (not including undo points), or -1 on
/// error. /// error.
///
/// ```c
/// EXTERN_C int64_t tc_replica_num_local_operations(struct TCReplica *rep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_num_local_operations(rep: *mut TCReplica) -> i64 { pub unsafe extern "C" fn tc_replica_num_local_operations(rep: *mut TCReplica) -> i64 {
wrap( wrap(
@ -376,7 +454,13 @@ pub unsafe extern "C" fn tc_replica_num_local_operations(rep: *mut TCReplica) ->
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Get the number of undo points (number of undo calls possible), or -1 on error. /// Get the number of undo points (number of undo calls possible), or -1 on error.
///
/// ```c
/// EXTERN_C int64_t tc_replica_num_undo_points(struct TCReplica *rep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_num_undo_points(rep: *mut TCReplica) -> i64 { pub unsafe extern "C" fn tc_replica_num_undo_points(rep: *mut TCReplica) -> i64 {
wrap( wrap(
@ -389,10 +473,16 @@ pub unsafe extern "C" fn tc_replica_num_undo_points(rep: *mut TCReplica) -> i64
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Add an UndoPoint, if one has not already been added by this Replica. This occurs automatically /// Add an UndoPoint, if one has not already been added by this Replica. This occurs automatically
/// when a change is made. The `force` flag allows forcing a new UndoPoint even if one has already /// when a change is made. The `force` flag allows forcing a new UndoPoint even if one has already
/// been created by this Replica, and may be useful when a Replica instance is held for a long time /// been created by this Replica, and may be useful when a Replica instance is held for a long time
/// and used to apply more than one user-visible change. /// and used to apply more than one user-visible change.
///
/// ```c
/// EXTERN_C TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_add_undo_point(rep: *mut TCReplica, force: bool) -> TCResult { pub unsafe extern "C" fn tc_replica_add_undo_point(rep: *mut TCReplica, force: bool) -> TCResult {
wrap( wrap(
@ -405,9 +495,15 @@ pub unsafe extern "C" fn tc_replica_add_undo_point(rep: *mut TCReplica, force: b
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Rebuild this replica's working set, based on whether tasks are pending or not. If `renumber` /// Rebuild this replica's working set, based on whether tasks are pending or not. If `renumber`
/// is true, then existing tasks may be moved to new working-set indices; in any case, on /// is true, then existing tasks may be moved to new working-set indices; in any case, on
/// completion all pending tasks are in the working set and all non- pending tasks are not. /// completion all pending tasks are in the working set and all non- pending tasks are not.
///
/// ```c
/// EXTERN_C TCResult tc_replica_rebuild_working_set(struct TCReplica *rep, bool renumber);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_rebuild_working_set( pub unsafe extern "C" fn tc_replica_rebuild_working_set(
rep: *mut TCReplica, rep: *mut TCReplica,
@ -423,9 +519,15 @@ pub unsafe extern "C" fn tc_replica_rebuild_working_set(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Get the latest error for a replica, or a string with NULL ptr if no error exists. Subsequent /// Get the latest error for a replica, or a string with NULL ptr if no error exists. Subsequent
/// calls to this function will return NULL. The rep pointer must not be NULL. The caller must /// calls to this function will return NULL. The rep pointer must not be NULL. The caller must
/// free the returned string. /// free the returned string.
///
/// ```c
/// EXTERN_C struct TCString tc_replica_error(struct TCReplica *rep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_error(rep: *mut TCReplica) -> TCString { pub unsafe extern "C" fn tc_replica_error(rep: *mut TCReplica) -> TCString {
// SAFETY: // SAFETY:
@ -443,8 +545,14 @@ pub unsafe extern "C" fn tc_replica_error(rep: *mut TCReplica) -> TCString {
} }
} }
#[ffizz_header::item]
#[ffizz(order = 903)]
/// Free a replica. The replica may not be used after this function returns and must not be freed /// Free a replica. The replica may not be used after this function returns and must not be freed
/// more than once. /// more than once.
///
/// ```c
/// EXTERN_C void tc_replica_free(struct TCReplica *rep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_free(rep: *mut TCReplica) { pub unsafe extern "C" fn tc_replica_free(rep: *mut TCReplica) {
// SAFETY: // SAFETY:

View file

@ -1,7 +1,23 @@
#[ffizz_header::item]
#[ffizz(order = 100)]
/// ***** TCResult *****
///
/// A result from a TC operation. Typically if this value is TC_RESULT_ERROR, /// A result from a TC operation. Typically if this value is TC_RESULT_ERROR,
/// the associated object's `tc_.._error` method will return an error message. /// the associated object's `tc_.._error` method will return an error message.
/// cbindgen:prefix-with-name ///
/// cbindgen:rename-all=ScreamingSnakeCase /// ```c
/// enum TCResult
/// #ifdef __cplusplus
/// : int32_t
/// #endif // __cplusplus
/// {
/// TC_RESULT_ERROR = -1,
/// TC_RESULT_OK = 0,
/// };
/// #ifndef __cplusplus
/// typedef int32_t TCResult;
/// #endif // __cplusplus
/// ```
#[repr(i32)] #[repr(i32)]
pub enum TCResult { pub enum TCResult {
Error = -1, Error = -1,

View file

@ -3,12 +3,20 @@ use crate::types::*;
use crate::util::err_to_ruststring; use crate::util::err_to_ruststring;
use taskchampion::{Server, ServerConfig}; use taskchampion::{Server, ServerConfig};
#[ffizz_header::item]
#[ffizz(order = 800)]
/// ***** TCServer *****
///
/// TCServer represents an interface to a sync server. Aside from new and free, a server /// TCServer represents an interface to a sync server. Aside from new and free, a server
/// has no C-accessible API, but is designed to be passed to `tc_replica_sync`. /// has no C-accessible API, but is designed to be passed to `tc_replica_sync`.
/// ///
/// ## Safety /// ## Safety
/// ///
/// TCServer are not threadsafe, and must not be used with multiple replicas simultaneously. /// TCServer are not threadsafe, and must not be used with multiple replicas simultaneously.
///
/// ```c
/// typedef struct TCServer TCServer;
/// ```
pub struct TCServer(Box<dyn Server>); pub struct TCServer(Box<dyn Server>);
impl PassByPointer for TCServer {} impl PassByPointer for TCServer {}
@ -53,6 +61,8 @@ where
} }
} }
#[ffizz_header::item]
#[ffizz(order = 801)]
/// Create a new TCServer that operates locally (on-disk). See the TaskChampion docs for the /// Create a new TCServer that operates locally (on-disk). See the TaskChampion docs for the
/// description of the arguments. /// description of the arguments.
/// ///
@ -60,6 +70,10 @@ where
/// returned. The caller must free this string. /// returned. The caller must free this string.
/// ///
/// The server must be freed after it is used - tc_replica_sync does not automatically free it. /// The server must be freed after it is used - tc_replica_sync does not automatically free it.
///
/// ```c
/// EXTERN_C struct TCServer *tc_server_new_local(struct TCString server_dir, struct TCString *error_out);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_server_new_local( pub unsafe extern "C" fn tc_server_new_local(
server_dir: TCString, server_dir: TCString,
@ -83,6 +97,8 @@ pub unsafe extern "C" fn tc_server_new_local(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 801)]
/// Create a new TCServer that connects to a remote server. See the TaskChampion docs for the /// Create a new TCServer that connects to a remote server. See the TaskChampion docs for the
/// description of the arguments. /// description of the arguments.
/// ///
@ -90,6 +106,13 @@ pub unsafe extern "C" fn tc_server_new_local(
/// returned. The caller must free this string. /// returned. The caller must free this string.
/// ///
/// The server must be freed after it is used - tc_replica_sync does not automatically free it. /// The server must be freed after it is used - tc_replica_sync does not automatically free it.
///
/// ```c
/// EXTERN_C struct TCServer *tc_server_new_remote(struct TCString origin,
/// struct TCUuid client_key,
/// struct TCString encryption_secret,
/// struct TCString *error_out);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_server_new_remote( pub unsafe extern "C" fn tc_server_new_remote(
origin: TCString, origin: TCString,
@ -129,8 +152,14 @@ pub unsafe extern "C" fn tc_server_new_remote(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 802)]
/// Free a server. The server may not be used after this function returns and must not be freed /// Free a server. The server may not be used after this function returns and must not be freed
/// more than once. /// more than once.
///
/// ```c
/// EXTERN_C void tc_server_free(struct TCServer *server);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_server_free(server: *mut TCServer) { pub unsafe extern "C" fn tc_server_free(server: *mut TCServer) {
debug_assert!(!server.is_null()); debug_assert!(!server.is_null());

View file

@ -1,17 +1,38 @@
pub use taskchampion::Status; pub use taskchampion::Status;
#[ffizz_header::item]
#[ffizz(order = 700)]
/// ***** TCStatus *****
///
/// The status of a task, as defined by the task data model. /// The status of a task, as defined by the task data model.
/// cbindgen:prefix-with-name ///
/// cbindgen:rename-all=ScreamingSnakeCase /// ```c
#[repr(C)] /// #ifdef __cplusplus
/// typedef enum TCStatus : int32_t {
/// #else // __cplusplus
/// typedef int32_t TCStatus;
/// enum TCStatus {
/// #endif // __cplusplus
/// TC_STATUS_PENDING = 0,
/// TC_STATUS_COMPLETED = 1,
/// TC_STATUS_DELETED = 2,
/// TC_STATUS_RECURRING = 3,
/// // Unknown signifies a status in the task DB that was not
/// // recognized.
/// TC_STATUS_UNKNOWN = -1,
/// #ifdef __cplusplus
/// } TCStatus;
/// #else // __cplusplus
/// };
/// #endif // __cplusplus
/// ```
#[repr(i32)]
pub enum TCStatus { pub enum TCStatus {
Pending, Pending = 0,
Completed, Completed = 1,
Deleted, Deleted = 2,
Recurring, Recurring = 3,
/// Unknown signifies a status in the task DB that was not Unknown = -1,
/// recognized.
Unknown,
} }
impl From<TCStatus> for Status { impl From<TCStatus> for Status {

View file

@ -4,6 +4,10 @@ use std::ffi::{CStr, CString, OsString};
use std::os::raw::c_char; use std::os::raw::c_char;
use std::path::PathBuf; use std::path::PathBuf;
#[ffizz_header::item]
#[ffizz(order = 200)]
/// ***** TCString *****
///
/// TCString supports passing strings into and out of the TaskChampion API. /// TCString supports passing strings into and out of the TaskChampion API.
/// ///
/// # Rust Strings and C Strings /// # Rust Strings and C Strings
@ -41,7 +45,15 @@ use std::path::PathBuf;
/// for such a value. /// for such a value.
/// ///
/// TCString is not threadsafe. /// TCString is not threadsafe.
/// cbindgen:field-names=[ptr, _u1, _u2, _u3] ///
/// ```c
/// typedef struct TCString {
/// void *ptr; // opaque, but may be checked for NULL
/// size_t _u1; // reserved
/// size_t _u2; // reserved
/// uint8_t _u3; // reserved
/// } TCString;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCString { pub struct TCString {
// defined based on the type // defined based on the type
@ -360,19 +372,36 @@ where
rv rv
} }
#[ffizz_header::item]
#[ffizz(order = 210)]
/// ***** TCStringList *****
///
/// TCStringList represents a list of strings. /// TCStringList represents a list of strings.
/// ///
/// The content of this struct must be treated as read-only. /// The content of this struct must be treated as read-only.
///
/// ```c
/// typedef struct TCStringList {
/// // number of strings in items
/// size_t len;
/// // reserved
/// size_t _u1;
/// // 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.
/// struct TCString *items;
/// } TCStringList;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCStringList { pub struct TCStringList {
/// number of strings in items /// number of strings in items
len: libc::size_t, len: libc::size_t,
/// total size of items (internal use only) /// total size of items (internal use only)
_capacity: libc::size_t, capacity: libc::size_t,
/// TCStringList representing each string. these remain owned by the TCStringList instance and will /// Array of strings. These remain owned by the TCStringList instance and will be freed by
/// be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the /// tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the
/// *TCStringList at indexes 0..len-1 are not NULL. /// *TCStringList at indexes 0..len-1 are not NULL.
items: *mut TCString, items: *mut TCString,
} }
@ -383,7 +412,7 @@ impl CList for TCStringList {
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
TCStringList { TCStringList {
len, len,
_capacity: cap, capacity: cap,
items, items,
} }
} }
@ -398,10 +427,12 @@ impl CList for TCStringList {
} }
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
(self.items, self.len, self._capacity) (self.items, self.len, self.capacity)
} }
} }
#[ffizz_header::item]
#[ffizz(order = 201)]
/// Create a new TCString referencing the given C string. The C string must remain valid and /// 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 /// unchanged until after the TCString is freed. It's typically easiest to ensure this by using a
/// static string. /// static string.
@ -416,6 +447,10 @@ impl CList for TCStringList {
/// tc_task_annotate(task, tc_string_borrow(url)); // TCString created, passed, and freed /// tc_task_annotate(task, tc_string_borrow(url)); // TCString created, passed, and freed
/// free(url); // string is no longer referenced and can be freed /// free(url); // string is no longer referenced and can be freed
/// ``` /// ```
///
/// ```c
/// EXTERN_C struct TCString tc_string_borrow(const char *cstr);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_string_borrow(cstr: *const libc::c_char) -> TCString { pub unsafe extern "C" fn tc_string_borrow(cstr: *const libc::c_char) -> TCString {
debug_assert!(!cstr.is_null()); debug_assert!(!cstr.is_null());
@ -430,8 +465,14 @@ pub unsafe extern "C" fn tc_string_borrow(cstr: *const libc::c_char) -> TCString
unsafe { TCString::return_val(RustString::CStr(cstr)) } unsafe { TCString::return_val(RustString::CStr(cstr)) }
} }
#[ffizz_header::item]
#[ffizz(order = 201)]
/// 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
/// is independent of the given string, which can be freed or overwritten immediately. /// is independent of the given string, which can be freed or overwritten immediately.
///
/// ```c
/// EXTERN_C struct TCString tc_string_clone(const char *cstr);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_string_clone(cstr: *const libc::c_char) -> TCString { pub unsafe extern "C" fn tc_string_clone(cstr: *const libc::c_char) -> TCString {
debug_assert!(!cstr.is_null()); debug_assert!(!cstr.is_null());
@ -447,6 +488,8 @@ pub unsafe extern "C" fn tc_string_clone(cstr: *const libc::c_char) -> TCString
unsafe { TCString::return_val(RustString::CString(cstring)) } unsafe { TCString::return_val(RustString::CString(cstring)) }
} }
#[ffizz_header::item]
#[ffizz(order = 201)]
/// 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
/// of strings containing embedded NUL characters. As with `tc_string_clone`, the resulting /// of strings containing embedded NUL characters. As with `tc_string_clone`, the resulting
/// TCString is independent of the passed buffer, which may be reused or freed immediately. /// TCString is independent of the passed buffer, which may be reused or freed immediately.
@ -454,6 +497,10 @@ pub unsafe extern "C" fn tc_string_clone(cstr: *const libc::c_char) -> TCString
/// The length should _not_ include any trailing NUL. /// The length should _not_ include any trailing NUL.
/// ///
/// The given length must be less than half the maximum value of usize. /// The given length must be less than half the maximum value of usize.
///
/// ```c
/// EXTERN_C struct TCString tc_string_clone_with_len(const char *buf, size_t len);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_string_clone_with_len( pub unsafe extern "C" fn tc_string_clone_with_len(
buf: *const libc::c_char, buf: *const libc::c_char,
@ -477,6 +524,8 @@ pub unsafe extern "C" fn tc_string_clone_with_len(
unsafe { TCString::return_val(RustString::Bytes(vec)) } unsafe { TCString::return_val(RustString::Bytes(vec)) }
} }
#[ffizz_header::item]
#[ffizz(order = 201)]
/// Get the content of the string as a regular C string. The given string must be valid. The /// Get the content of the string as a regular C string. The given string must be valid. The
/// returned value is NULL if the string contains NUL bytes or (in some cases) invalid UTF-8. The /// returned value is NULL if the string contains NUL bytes or (in some cases) invalid UTF-8. The
/// returned C string is valid until the TCString is freed or passed to another TC API function. /// returned C string is valid until the TCString is freed or passed to another TC API function.
@ -488,6 +537,10 @@ pub unsafe extern "C" fn tc_string_clone_with_len(
/// terminator. The pointer must not be NULL. /// terminator. The pointer must not be NULL.
/// ///
/// This function does _not_ take ownership of the TCString. /// This function does _not_ take ownership of the TCString.
///
/// ```c
/// EXTERN_C const char *tc_string_content(const struct TCString *tcstring);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_string_content(tcstring: *const TCString) -> *const libc::c_char { pub unsafe extern "C" fn tc_string_content(tcstring: *const TCString) -> *const libc::c_char {
// SAFETY; // SAFETY;
@ -514,6 +567,8 @@ pub unsafe extern "C" fn tc_string_content(tcstring: *const TCString) -> *const
} }
} }
#[ffizz_header::item]
#[ffizz(order = 201)]
/// 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 TaskChampio /// returned buffer is valid until the TCString is freed or passed to another TaskChampio
@ -523,6 +578,10 @@ pub unsafe extern "C" fn tc_string_content(tcstring: *const TCString) -> *const
/// terminator. The pointer must not be NULL. /// terminator. The pointer must not be NULL.
/// ///
/// This function does _not_ take ownership of the TCString. /// This function does _not_ take ownership of the TCString.
///
/// ```c
/// EXTERN_C const char *tc_string_content_with_len(const struct TCString *tcstring, size_t *len_out);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_string_content_with_len( pub unsafe extern "C" fn tc_string_content_with_len(
tcstring: *const TCString, tcstring: *const TCString,
@ -546,8 +605,14 @@ pub unsafe extern "C" fn tc_string_content_with_len(
} }
} }
#[ffizz_header::item]
#[ffizz(order = 201)]
/// Free a TCString. The given string must not be NULL. The string must not be used /// Free a TCString. The given string must not be NULL. The string must not be used
/// after this function returns, and must not be freed more than once. /// after this function returns, and must not be freed more than once.
///
/// ```c
/// EXTERN_C void tc_string_free(struct TCString *tcstring);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_string_free(tcstring: *mut TCString) { pub unsafe extern "C" fn tc_string_free(tcstring: *mut TCString) {
// SAFETY: // SAFETY:
@ -556,10 +621,16 @@ pub unsafe extern "C" fn tc_string_free(tcstring: *mut TCString) {
drop(unsafe { TCString::take_val_from_arg(tcstring, TCString::default()) }); drop(unsafe { TCString::take_val_from_arg(tcstring, TCString::default()) });
} }
#[ffizz_header::item]
#[ffizz(order = 211)]
/// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used after /// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used after
/// this call. /// this call.
/// ///
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList. /// When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList.
///
/// ```c
/// EXTERN_C void tc_string_list_free(struct TCStringList *tcstrings);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_string_list_free(tcstrings: *mut TCStringList) { pub unsafe extern "C" fn tc_string_list_free(tcstrings: *mut TCStringList) {
// SAFETY: // SAFETY:
@ -579,7 +650,7 @@ mod test {
let tcstrings = unsafe { TCStringList::return_val(Vec::new()) }; let tcstrings = unsafe { TCStringList::return_val(Vec::new()) };
assert!(!tcstrings.items.is_null()); assert!(!tcstrings.items.is_null());
assert_eq!(tcstrings.len, 0); assert_eq!(tcstrings.len, 0);
assert_eq!(tcstrings._capacity, 0); assert_eq!(tcstrings.capacity, 0);
} }
#[test] #[test]
@ -589,7 +660,7 @@ mod test {
unsafe { tc_string_list_free(&mut tcstrings) }; unsafe { tc_string_list_free(&mut tcstrings) };
assert!(tcstrings.items.is_null()); assert!(tcstrings.items.is_null());
assert_eq!(tcstrings.len, 0); assert_eq!(tcstrings.len, 0);
assert_eq!(tcstrings._capacity, 0); assert_eq!(tcstrings.capacity, 0);
} }
const INVALID_UTF8: &[u8] = b"abc\xf0\x28\x8c\x28"; const INVALID_UTF8: &[u8] = b"abc\xf0\x28\x8c\x28";

View file

@ -7,6 +7,10 @@ use std::ptr::NonNull;
use std::str::FromStr; use std::str::FromStr;
use taskchampion::{utc_timestamp, Annotation, Tag, Task, TaskMut, Uuid}; use taskchampion::{utc_timestamp, Annotation, Tag, Task, TaskMut, Uuid};
#[ffizz_header::item]
#[ffizz(order = 1000)]
/// ***** TCTask *****
///
/// A task, as publicly exposed by this library. /// A task, as publicly exposed by this library.
/// ///
/// A task begins in "immutable" mode. It must be converted to "mutable" mode /// A task begins in "immutable" mode. It must be converted to "mutable" mode
@ -36,6 +40,10 @@ use taskchampion::{utc_timestamp, Annotation, Tag, Task, TaskMut, Uuid};
/// Once passed to tc_task_free, a `*TCTask` becomes invalid and must not be used again. /// Once passed to tc_task_free, a `*TCTask` becomes invalid and must not be used again.
/// ///
/// TCTasks are not threadsafe. /// TCTasks are not threadsafe.
///
/// ```c
/// typedef struct TCTask TCTask;
/// ```
pub struct TCTask { pub struct TCTask {
/// The wrapped Task or TaskMut /// The wrapped Task or TaskMut
inner: Inner, inner: Inner,
@ -168,23 +176,40 @@ impl TryFrom<RustString<'static>> for Tag {
} }
} }
#[ffizz_header::item]
#[ffizz(order = 1010)]
/// ***** TCTaskList *****
///
/// TCTaskList represents a list of tasks. /// TCTaskList represents a list of tasks.
/// ///
/// The content of this struct must be treated as read-only: no fields or anything they reference /// The content of this struct must be treated as read-only: no fields or anything they reference
/// should be modified directly by C code. /// should be modified directly by C code.
/// ///
/// When an item is taken from this list, its pointer in `items` is set to NULL. /// When an item is taken from this list, its pointer in `items` is set to NULL.
///
/// ```c
/// typedef struct TCTaskList {
/// // number of tasks in items
/// size_t len;
/// // reserved
/// size_t _u1;
/// // 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.
/// // Pointers in the array may be NULL after `tc_task_list_take`.
/// struct TCTask **items;
/// } TCTaskList;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCTaskList { pub struct TCTaskList {
/// number of tasks in items /// number of tasks in items
len: libc::size_t, len: libc::size_t,
/// total size of items (internal use only) /// total size of items (internal use only)
_capacity: libc::size_t, capacity: libc::size_t,
/// array of pointers representing each task. these remain owned by the TCTaskList instance and /// 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, /// 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. /// Pointers in the array may be NULL after `tc_task_list_take`.
items: *mut Option<NonNull<TCTask>>, items: *mut Option<NonNull<TCTask>>,
} }
@ -194,7 +219,7 @@ impl CList for TCTaskList {
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
TCTaskList { TCTaskList {
len, len,
_capacity: cap, capacity: cap,
items, items,
} }
} }
@ -209,55 +234,18 @@ impl CList for TCTaskList {
} }
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
(self.items, self.len, self._capacity) (self.items, self.len, self.capacity)
} }
} }
/// Convert an immutable task into a mutable task. #[ffizz_header::item]
/// #[ffizz(order = 1001)]
/// The task must not be NULL. It is modified in-place, and becomes mutable. /// Get a task's UUID.
///
/// The replica must not be NULL. After this function returns, the replica _cannot be used at all_
/// until this task is made immutable again. This implies that it is not allowed for more than one
/// task associated with a replica to be mutable at any time.
///
/// Typical mutation of tasks is bracketed with `tc_task_to_mut` and `tc_task_to_immut`:
/// ///
/// ```c /// ```c
/// tc_task_to_mut(task, rep); /// EXTERN_C struct TCUuid tc_task_get_uuid(struct TCTask *task);
/// success = tc_task_done(task);
/// tc_task_to_immut(task, rep);
/// if (!success) { ... }
/// ``` /// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_to_mut(task: *mut TCTask, tcreplica: *mut TCReplica) {
// SAFETY:
// - task is not null (promised by caller)
// - task outlives 'a (promised by caller)
let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
// SAFETY:
// - tcreplica is not NULL (promised by caller)
// - tcreplica lives until later call to to_immut via tc_task_to_immut (promised by caller,
// who cannot call tc_replica_free during this time)
unsafe { tctask.to_mut(tcreplica) };
}
/// Convert a mutable task into an immutable task.
///
/// The task must not be NULL. It is modified in-place, and becomes immutable.
///
/// The replica passed to `tc_task_to_mut` may be used freely after this call.
#[no_mangle]
pub unsafe extern "C" fn tc_task_to_immut(task: *mut TCTask) {
// SAFETY:
// - task is not null (promised by caller)
// - task outlives 'a (promised by caller)
let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
tctask.to_immut();
}
/// Get a task's UUID.
#[no_mangle]
pub unsafe extern "C" fn tc_task_get_uuid(task: *mut TCTask) -> TCUuid { pub unsafe extern "C" fn tc_task_get_uuid(task: *mut TCTask) -> TCUuid {
wrap(task, |task| { wrap(task, |task| {
// SAFETY: // SAFETY:
@ -266,15 +254,27 @@ pub unsafe extern "C" fn tc_task_get_uuid(task: *mut TCTask) -> TCUuid {
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get a task's status. /// Get a task's status.
///
/// ```c
/// EXTERN_C enum TCStatus tc_task_get_status(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_status(task: *mut TCTask) -> TCStatus { pub unsafe extern "C" fn tc_task_get_status(task: *mut TCTask) -> TCStatus {
wrap(task, |task| task.get_status().into()) wrap(task, |task| task.get_status().into())
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get the underlying key/value pairs for this task. The returned TCKVList is /// Get the underlying key/value pairs for this task. The returned TCKVList is
/// a "snapshot" of the task and will not be updated if the task is subsequently /// a "snapshot" of the task and will not be updated if the task is subsequently
/// modified. It is the caller's responsibility to free the TCKVList. /// modified. It is the caller's responsibility to free the TCKVList.
///
/// ```c
/// EXTERN_C struct TCKVList tc_task_get_taskmap(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_taskmap(task: *mut TCTask) -> TCKVList { pub unsafe extern "C" fn tc_task_get_taskmap(task: *mut TCTask) -> TCKVList {
wrap(task, |task| { wrap(task, |task| {
@ -293,7 +293,13 @@ pub unsafe extern "C" fn tc_task_get_taskmap(task: *mut TCTask) -> TCKVList {
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get a task's description. /// Get a task's description.
///
/// ```c
/// EXTERN_C struct TCString tc_task_get_description(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_description(task: *mut TCTask) -> TCString { pub unsafe extern "C" fn tc_task_get_description(task: *mut TCTask) -> TCString {
wrap(task, |task| { wrap(task, |task| {
@ -304,8 +310,14 @@ pub unsafe extern "C" fn tc_task_get_description(task: *mut TCTask) -> TCString
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get a task property's value, or NULL if the task has no such property, (including if the /// Get a task property's value, or NULL if the task has no such property, (including if the
/// property name is not valid utf-8). /// property name is not valid utf-8).
///
/// ```c
/// EXTERN_C struct TCString tc_task_get_value(struct TCTask *task, struct TCString property);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_value(task: *mut TCTask, property: TCString) -> TCString { pub unsafe extern "C" fn tc_task_get_value(task: *mut TCTask, property: TCString) -> TCString {
// SAFETY: // SAFETY:
@ -325,50 +337,98 @@ pub unsafe extern "C" fn tc_task_get_value(task: *mut TCTask, property: TCString
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get the entry timestamp for a task (when it was created), or 0 if not set. /// Get the entry timestamp for a task (when it was created), or 0 if not set.
///
/// ```c
/// EXTERN_C time_t tc_task_get_entry(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_entry(task: *mut TCTask) -> libc::time_t { 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())) wrap(task, |task| libc::time_t::as_ctype(task.get_entry()))
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get the wait timestamp for a task, or 0 if not set. /// Get the wait timestamp for a task, or 0 if not set.
///
/// ```c
/// EXTERN_C time_t tc_task_get_wait(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_wait(task: *mut TCTask) -> libc::time_t { 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())) wrap(task, |task| libc::time_t::as_ctype(task.get_wait()))
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get the modified timestamp for a task, or 0 if not set. /// Get the modified timestamp for a task, or 0 if not set.
///
/// ```c
/// EXTERN_C time_t tc_task_get_modified(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_modified(task: *mut TCTask) -> libc::time_t { 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())) wrap(task, |task| libc::time_t::as_ctype(task.get_modified()))
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Check if a task is waiting. /// Check if a task is waiting.
///
/// ```c
/// EXTERN_C bool tc_task_is_waiting(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_is_waiting(task: *mut TCTask) -> bool { pub unsafe extern "C" fn tc_task_is_waiting(task: *mut TCTask) -> bool {
wrap(task, |task| task.is_waiting()) wrap(task, |task| task.is_waiting())
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Check if a task is active (started and not stopped). /// Check if a task is active (started and not stopped).
///
/// ```c
/// EXTERN_C bool tc_task_is_active(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_is_active(task: *mut TCTask) -> bool { pub unsafe extern "C" fn tc_task_is_active(task: *mut TCTask) -> bool {
wrap(task, |task| task.is_active()) wrap(task, |task| task.is_active())
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Check if a task is blocked (depends on at least one other task). /// Check if a task is blocked (depends on at least one other task).
///
/// ```c
/// EXTERN_C bool tc_task_is_blocked(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_is_blocked(task: *mut TCTask) -> bool { pub unsafe extern "C" fn tc_task_is_blocked(task: *mut TCTask) -> bool {
wrap(task, |task| task.is_blocked()) wrap(task, |task| task.is_blocked())
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Check if a task is blocking (at least one other task depends on it). /// Check if a task is blocking (at least one other task depends on it).
///
/// ```c
/// EXTERN_C bool tc_task_is_blocking(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_is_blocking(task: *mut TCTask) -> bool { pub unsafe extern "C" fn tc_task_is_blocking(task: *mut TCTask) -> bool {
wrap(task, |task| task.is_blocking()) wrap(task, |task| task.is_blocking())
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Check if a task has the given tag. If the tag is invalid, this function will return false, as /// Check if a task has the given tag. If the tag is invalid, this function will return false, as
/// that (invalid) tag is not present. No error will be reported via `tc_task_error`. /// that (invalid) tag is not present. No error will be reported via `tc_task_error`.
///
/// ```c
/// EXTERN_C bool tc_task_has_tag(struct TCTask *task, struct TCString tag);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_has_tag(task: *mut TCTask, tag: TCString) -> bool { pub unsafe extern "C" fn tc_task_has_tag(task: *mut TCTask, tag: TCString) -> bool {
// SAFETY: // SAFETY:
@ -384,10 +444,16 @@ pub unsafe extern "C" fn tc_task_has_tag(task: *mut TCTask, tag: TCString) -> bo
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get the tags for the task. /// Get the tags for the task.
/// ///
/// The caller must free the returned TCStringList instance. The TCStringList instance does not /// The caller must free the returned TCStringList instance. The TCStringList instance does not
/// reference the task and the two may be freed in any order. /// reference the task and the two may be freed in any order.
///
/// ```c
/// EXTERN_C struct TCStringList tc_task_get_tags(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_tags(task: *mut TCTask) -> TCStringList { pub unsafe extern "C" fn tc_task_get_tags(task: *mut TCTask) -> TCStringList {
wrap(task, |task| { wrap(task, |task| {
@ -405,10 +471,16 @@ pub unsafe extern "C" fn tc_task_get_tags(task: *mut TCTask) -> TCStringList {
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get the annotations for the task. /// Get the annotations for the task.
/// ///
/// The caller must free the returned TCAnnotationList instance. The TCStringList instance does not /// The caller must free the returned TCAnnotationList instance. The TCStringList instance does not
/// reference the task and the two may be freed in any order. /// reference the task and the two may be freed in any order.
///
/// ```c
/// EXTERN_C struct TCAnnotationList tc_task_get_annotations(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_annotations(task: *mut TCTask) -> TCAnnotationList { pub unsafe extern "C" fn tc_task_get_annotations(task: *mut TCTask) -> TCAnnotationList {
wrap(task, |task| { wrap(task, |task| {
@ -425,9 +497,15 @@ pub unsafe extern "C" fn tc_task_get_annotations(task: *mut TCTask) -> TCAnnotat
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get the named UDA from the task. /// Get the named UDA from the task.
/// ///
/// Returns a TCString with NULL ptr field if the UDA does not exist. /// Returns a TCString with NULL ptr field if the UDA does not exist.
///
/// ```c
/// EXTERN_C struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, struct TCString key);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_uda( pub unsafe extern "C" fn tc_task_get_uda(
task: *mut TCTask, task: *mut TCTask,
@ -452,9 +530,15 @@ pub unsafe extern "C" fn tc_task_get_uda(
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get the named legacy UDA from the task. /// Get the named legacy UDA from the task.
/// ///
/// Returns NULL if the UDA does not exist. /// Returns NULL if the UDA does not exist.
///
/// ```c
/// EXTERN_C struct TCString tc_task_get_legacy_uda(struct TCTask *task, struct TCString key);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_legacy_uda(task: *mut TCTask, key: TCString) -> TCString { pub unsafe extern "C" fn tc_task_get_legacy_uda(task: *mut TCTask, key: TCString) -> TCString {
wrap(task, |task| { wrap(task, |task| {
@ -472,9 +556,15 @@ pub unsafe extern "C" fn tc_task_get_legacy_uda(task: *mut TCTask, key: TCString
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get all UDAs for this task. /// Get all UDAs for this task.
/// ///
/// Legacy UDAs are represented with an empty string in the ns field. /// Legacy UDAs are represented with an empty string in the ns field.
///
/// ```c
/// EXTERN_C struct TCUdaList tc_task_get_udas(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_udas(task: *mut TCTask) -> TCUdaList { pub unsafe extern "C" fn tc_task_get_udas(task: *mut TCTask) -> TCUdaList {
wrap(task, |task| { wrap(task, |task| {
@ -498,10 +588,16 @@ pub unsafe extern "C" fn tc_task_get_udas(task: *mut TCTask) -> TCUdaList {
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get all UDAs for this task. /// Get all UDAs for this task.
/// ///
/// All TCUdas in this list have a NULL ns field. The entire UDA key is /// All TCUdas in this list have a NULL ns field. The entire UDA key is
/// included in the key field. The caller must free the returned list. /// included in the key field. The caller must free the returned list.
///
/// ```c
/// EXTERN_C struct TCUdaList tc_task_get_legacy_udas(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_get_legacy_udas(task: *mut TCTask) -> TCUdaList { pub unsafe extern "C" fn tc_task_get_legacy_udas(task: *mut TCTask) -> TCUdaList {
wrap(task, |task| { wrap(task, |task| {
@ -525,7 +621,70 @@ pub unsafe extern "C" fn tc_task_get_legacy_udas(task: *mut TCTask) -> TCUdaList
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1001)]
/// Get all dependencies for a task.
///
/// ```c
/// EXTERN_C struct TCUuidList tc_task_get_dependencies(struct TCTask *task);
/// ```
#[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| {
// SAFETY:
// - value is not allocated
unsafe { TCUuid::return_val(u) }
})
.collect();
// SAFETY:
// - caller will free this list
unsafe { TCUuidList::return_val(vec) }
})
}
#[ffizz_header::item]
#[ffizz(order = 1002)]
/// Convert an immutable task into a mutable task.
///
/// The task must not be NULL. It is modified in-place, and becomes mutable.
///
/// The replica must not be NULL. After this function returns, the replica _cannot be used at all_
/// until this task is made immutable again. This implies that it is not allowed for more than one
/// task associated with a replica to be mutable at any time.
///
/// Typical mutation of tasks is bracketed with `tc_task_to_mut` and `tc_task_to_immut`:
///
/// tc_task_to_mut(task, rep);
/// success = tc_task_done(task);
/// tc_task_to_immut(task, rep);
/// if (!success) { ... }
///
/// ```c
/// EXTERN_C void tc_task_to_mut(struct TCTask *task, struct TCReplica *tcreplica);
/// ```
#[no_mangle]
pub unsafe extern "C" fn tc_task_to_mut(task: *mut TCTask, tcreplica: *mut TCReplica) {
// SAFETY:
// - task is not null (promised by caller)
// - task outlives 'a (promised by caller)
let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
// SAFETY:
// - tcreplica is not NULL (promised by caller)
// - tcreplica lives until later call to to_immut via tc_task_to_immut (promised by caller,
// who cannot call tc_replica_free during this time)
unsafe { tctask.to_mut(tcreplica) };
}
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Set a mutable task's status. /// Set a mutable task's status.
///
/// ```c
/// EXTERN_C TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_set_status(task: *mut TCTask, status: TCStatus) -> TCResult { pub unsafe extern "C" fn tc_task_set_status(task: *mut TCTask, status: TCStatus) -> TCResult {
wrap_mut( wrap_mut(
@ -538,7 +697,13 @@ pub unsafe extern "C" fn tc_task_set_status(task: *mut TCTask, status: TCStatus)
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Set a mutable task's property value by name. If value.ptr is NULL, the property is removed. /// Set a mutable task's property value by name. If value.ptr is NULL, the property is removed.
///
/// ```c
/// EXTERN_C TCResult tc_task_set_value(struct TCTask *task, struct TCString property, struct TCString value);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_set_value( pub unsafe extern "C" fn tc_task_set_value(
task: *mut TCTask, task: *mut TCTask,
@ -572,7 +737,13 @@ pub unsafe extern "C" fn tc_task_set_value(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Set a mutable task's description. /// Set a mutable task's description.
///
/// ```c
/// EXTERN_C TCResult tc_task_set_description(struct TCTask *task, struct TCString description);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_set_description( pub unsafe extern "C" fn tc_task_set_description(
task: *mut TCTask, task: *mut TCTask,
@ -592,8 +763,14 @@ pub unsafe extern "C" fn tc_task_set_description(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Set a mutable task's entry (creation time). Pass entry=0 to unset /// Set a mutable task's entry (creation time). Pass entry=0 to unset
/// the entry field. /// the entry field.
///
/// ```c
/// EXTERN_C TCResult tc_task_set_entry(struct TCTask *task, time_t entry);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_set_entry(task: *mut TCTask, entry: libc::time_t) -> TCResult { pub unsafe extern "C" fn tc_task_set_entry(task: *mut TCTask, entry: libc::time_t) -> TCResult {
wrap_mut( wrap_mut(
@ -607,7 +784,13 @@ pub unsafe extern "C" fn tc_task_set_entry(task: *mut TCTask, entry: libc::time_
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Set a mutable task's wait timestamp. Pass wait=0 to unset the wait field. /// Set a mutable task's wait timestamp. Pass wait=0 to unset the wait field.
///
/// ```c
/// EXTERN_C TCResult tc_task_set_wait(struct TCTask *task, time_t wait);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_set_wait(task: *mut TCTask, wait: libc::time_t) -> TCResult { pub unsafe extern "C" fn tc_task_set_wait(task: *mut TCTask, wait: libc::time_t) -> TCResult {
wrap_mut( wrap_mut(
@ -621,7 +804,13 @@ pub unsafe extern "C" fn tc_task_set_wait(task: *mut TCTask, wait: libc::time_t)
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Set a mutable task's modified timestamp. The value cannot be zero. /// Set a mutable task's modified timestamp. The value cannot be zero.
///
/// ```c
/// EXTERN_C TCResult tc_task_set_modified(struct TCTask *task, time_t modified);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_set_modified( pub unsafe extern "C" fn tc_task_set_modified(
task: *mut TCTask, task: *mut TCTask,
@ -641,7 +830,13 @@ pub unsafe extern "C" fn tc_task_set_modified(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Start a task. /// Start a task.
///
/// ```c
/// EXTERN_C TCResult tc_task_start(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_start(task: *mut TCTask) -> TCResult { pub unsafe extern "C" fn tc_task_start(task: *mut TCTask) -> TCResult {
wrap_mut( wrap_mut(
@ -654,7 +849,13 @@ pub unsafe extern "C" fn tc_task_start(task: *mut TCTask) -> TCResult {
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Stop a task. /// Stop a task.
///
/// ```c
/// EXTERN_C TCResult tc_task_stop(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_stop(task: *mut TCTask) -> TCResult { pub unsafe extern "C" fn tc_task_stop(task: *mut TCTask) -> TCResult {
wrap_mut( wrap_mut(
@ -667,7 +868,13 @@ pub unsafe extern "C" fn tc_task_stop(task: *mut TCTask) -> TCResult {
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Mark a task as done. /// Mark a task as done.
///
/// ```c
/// EXTERN_C TCResult tc_task_done(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_done(task: *mut TCTask) -> TCResult { pub unsafe extern "C" fn tc_task_done(task: *mut TCTask) -> TCResult {
wrap_mut( wrap_mut(
@ -680,7 +887,13 @@ pub unsafe extern "C" fn tc_task_done(task: *mut TCTask) -> TCResult {
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Mark a task as deleted. /// Mark a task as deleted.
///
/// ```c
/// EXTERN_C TCResult tc_task_delete(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_delete(task: *mut TCTask) -> TCResult { pub unsafe extern "C" fn tc_task_delete(task: *mut TCTask) -> TCResult {
wrap_mut( wrap_mut(
@ -693,7 +906,13 @@ pub unsafe extern "C" fn tc_task_delete(task: *mut TCTask) -> TCResult {
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Add a tag to a mutable task. /// Add a tag to a mutable task.
///
/// ```c
/// EXTERN_C TCResult tc_task_add_tag(struct TCTask *task, struct TCString tag);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: TCString) -> TCResult { pub unsafe extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: TCString) -> TCResult {
// SAFETY: // SAFETY:
@ -711,7 +930,13 @@ pub unsafe extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: TCString) -> TC
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Remove a tag from a mutable task. /// Remove a tag from a mutable task.
///
/// ```c
/// EXTERN_C TCResult tc_task_remove_tag(struct TCTask *task, struct TCString tag);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_remove_tag(task: *mut TCTask, tag: TCString) -> TCResult { pub unsafe extern "C" fn tc_task_remove_tag(task: *mut TCTask, tag: TCString) -> TCResult {
// SAFETY: // SAFETY:
@ -729,8 +954,14 @@ pub unsafe extern "C" fn tc_task_remove_tag(task: *mut TCTask, tag: TCString) ->
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Add an annotation to a mutable task. This call takes ownership of the /// Add an annotation to a mutable task. This call takes ownership of the
/// passed annotation, which must not be used after the call returns. /// passed annotation, which must not be used after the call returns.
///
/// ```c
/// EXTERN_C TCResult tc_task_add_annotation(struct TCTask *task, struct TCAnnotation *annotation);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_add_annotation( pub unsafe extern "C" fn tc_task_add_annotation(
task: *mut TCTask, task: *mut TCTask,
@ -753,7 +984,13 @@ pub unsafe extern "C" fn tc_task_add_annotation(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Remove an annotation from a mutable task. /// Remove an annotation from a mutable task.
///
/// ```c
/// EXTERN_C TCResult tc_task_remove_annotation(struct TCTask *task, int64_t entry);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_remove_annotation(task: *mut TCTask, entry: i64) -> TCResult { pub unsafe extern "C" fn tc_task_remove_annotation(task: *mut TCTask, entry: i64) -> TCResult {
wrap_mut( wrap_mut(
@ -766,7 +1003,16 @@ pub unsafe extern "C" fn tc_task_remove_annotation(task: *mut TCTask, entry: i64
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Set a UDA on a mutable task. /// Set a UDA on a mutable task.
///
/// ```c
/// EXTERN_C TCResult tc_task_set_uda(struct TCTask *task,
/// struct TCString ns,
/// struct TCString key,
/// struct TCString value);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_set_uda( pub unsafe extern "C" fn tc_task_set_uda(
task: *mut TCTask, task: *mut TCTask,
@ -792,7 +1038,13 @@ pub unsafe extern "C" fn tc_task_set_uda(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Remove a UDA fraom a mutable task. /// Remove a UDA fraom a mutable task.
///
/// ```c
/// EXTERN_C TCResult tc_task_remove_uda(struct TCTask *task, struct TCString ns, struct TCString key);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_remove_uda( pub unsafe extern "C" fn tc_task_remove_uda(
task: *mut TCTask, task: *mut TCTask,
@ -815,7 +1067,13 @@ pub unsafe extern "C" fn tc_task_remove_uda(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Set a legacy UDA on a mutable task. /// Set a legacy UDA on a mutable task.
///
/// ```c
/// EXTERN_C TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, struct TCString value);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_set_legacy_uda( pub unsafe extern "C" fn tc_task_set_legacy_uda(
task: *mut TCTask, task: *mut TCTask,
@ -838,7 +1096,13 @@ pub unsafe extern "C" fn tc_task_set_legacy_uda(
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Remove a UDA fraom a mutable task. /// Remove a UDA fraom a mutable task.
///
/// ```c
/// EXTERN_C TCResult tc_task_remove_legacy_uda(struct TCTask *task, struct TCString key);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_remove_legacy_uda(task: *mut TCTask, key: TCString) -> TCResult { pub unsafe extern "C" fn tc_task_remove_legacy_uda(task: *mut TCTask, key: TCString) -> TCResult {
// safety: // safety:
@ -855,25 +1119,13 @@ pub unsafe extern "C" fn tc_task_remove_legacy_uda(task: *mut TCTask, key: TCStr
) )
} }
/// Get all dependencies for a task. #[ffizz_header::item]
#[no_mangle] #[ffizz(order = 1003)]
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| {
// SAFETY:
// - value is not allocated
unsafe { TCUuid::return_val(u) }
})
.collect();
// SAFETY:
// - caller will free this list
unsafe { TCUuidList::return_val(vec) }
})
}
/// Add a dependency. /// Add a dependency.
///
/// ```c
/// EXTERN_C TCResult tc_task_add_dependency(struct TCTask *task, struct TCUuid dep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_add_dependency(task: *mut TCTask, dep: TCUuid) -> TCResult { pub unsafe extern "C" fn tc_task_add_dependency(task: *mut TCTask, dep: TCUuid) -> TCResult {
// SAFETY: // SAFETY:
@ -889,7 +1141,13 @@ pub unsafe extern "C" fn tc_task_add_dependency(task: *mut TCTask, dep: TCUuid)
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1003)]
/// Remove a dependency. /// Remove a dependency.
///
/// ```c
/// EXTERN_C TCResult tc_task_remove_dependency(struct TCTask *task, struct TCUuid dep);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_remove_dependency(task: *mut TCTask, dep: TCUuid) -> TCResult { pub unsafe extern "C" fn tc_task_remove_dependency(task: *mut TCTask, dep: TCUuid) -> TCResult {
// SAFETY: // SAFETY:
@ -905,9 +1163,35 @@ pub unsafe extern "C" fn tc_task_remove_dependency(task: *mut TCTask, dep: TCUui
) )
} }
#[ffizz_header::item]
#[ffizz(order = 1004)]
/// Convert a mutable task into an immutable task.
///
/// The task must not be NULL. It is modified in-place, and becomes immutable.
///
/// The replica passed to `tc_task_to_mut` may be used freely after this call.
///
/// ```c
/// EXTERN_C void tc_task_to_immut(struct TCTask *task);
/// ```
#[no_mangle]
pub unsafe extern "C" fn tc_task_to_immut(task: *mut TCTask) {
// SAFETY:
// - task is not null (promised by caller)
// - task outlives 'a (promised by caller)
let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) };
tctask.to_immut();
}
#[ffizz_header::item]
#[ffizz(order = 1005)]
/// Get the latest error for a task, or a string NULL ptr field if the last operation succeeded. /// Get the latest error for a task, or a string NULL ptr field if the last operation succeeded.
/// Subsequent calls to this function will return NULL. The task pointer must not be NULL. The /// Subsequent calls to this function will return NULL. The task pointer must not be NULL. The
/// caller must free the returned string. /// caller must free the returned string.
///
/// ```c
/// EXTERN_C struct TCString tc_task_error(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_error(task: *mut TCTask) -> TCString { pub unsafe extern "C" fn tc_task_error(task: *mut TCTask) -> TCString {
// SAFETY: // SAFETY:
@ -923,10 +1207,16 @@ pub unsafe extern "C" fn tc_task_error(task: *mut TCTask) -> TCString {
} }
} }
#[ffizz_header::item]
#[ffizz(order = 1006)]
/// Free a task. The given task must not be NULL. The task must not be used after this function /// Free a task. The given task must not be NULL. The task must not be used after this function
/// returns, and must not be freed more than once. /// returns, and must not be freed more than once.
/// ///
/// If the task is currently mutable, it will first be made immutable. /// If the task is currently mutable, it will first be made immutable.
///
/// ```c
/// EXTERN_C void tc_task_free(struct TCTask *task);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_free(task: *mut TCTask) { pub unsafe extern "C" fn tc_task_free(task: *mut TCTask) {
// SAFETY: // SAFETY:
@ -940,6 +1230,8 @@ pub unsafe extern "C" fn tc_task_free(task: *mut TCTask) {
drop(tctask); drop(tctask);
} }
#[ffizz_header::item]
#[ffizz(order = 1011)]
/// Take an item from a TCTaskList. After this call, the indexed item is no longer associated /// Take an item from a TCTaskList. After this call, the indexed item is no longer associated
/// with the list and becomes the caller's responsibility, just as if it had been returned from /// with the list and becomes the caller's responsibility, just as if it had been returned from
/// `tc_replica_get_task`. /// `tc_replica_get_task`.
@ -949,6 +1241,10 @@ pub unsafe extern "C" fn tc_task_free(task: *mut TCTask) {
/// index is out of bounds, this function will also return NULL. /// index is out of bounds, this function will also return NULL.
/// ///
/// The passed TCTaskList remains owned by the caller. /// The passed TCTaskList remains owned by the caller.
///
/// ```c
/// EXTERN_C struct TCTask *tc_task_list_take(struct TCTaskList *tasks, size_t index);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_list_take(tasks: *mut TCTaskList, index: usize) -> *mut TCTask { pub unsafe extern "C" fn tc_task_list_take(tasks: *mut TCTaskList, index: usize) -> *mut TCTask {
// SAFETY: // SAFETY:
@ -962,10 +1258,16 @@ pub unsafe extern "C" fn tc_task_list_take(tasks: *mut TCTaskList, index: usize)
} }
} }
#[ffizz_header::item]
#[ffizz(order = 1011)]
/// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after /// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after
/// this call. /// this call.
/// ///
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList. /// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList.
///
/// ```c
/// EXTERN_C void tc_task_list_free(struct TCTaskList *tasks);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_task_list_free(tasks: *mut TCTaskList) { pub unsafe extern "C" fn tc_task_list_free(tasks: *mut TCTaskList) {
// SAFETY: // SAFETY:
@ -984,7 +1286,7 @@ mod test {
let tasks = unsafe { TCTaskList::return_val(Vec::new()) }; let tasks = unsafe { TCTaskList::return_val(Vec::new()) };
assert!(!tasks.items.is_null()); assert!(!tasks.items.is_null());
assert_eq!(tasks.len, 0); assert_eq!(tasks.len, 0);
assert_eq!(tasks._capacity, 0); assert_eq!(tasks.capacity, 0);
} }
#[test] #[test]
@ -994,6 +1296,6 @@ mod test {
unsafe { tc_task_list_free(&mut tasks) }; unsafe { tc_task_list_free(&mut tasks) };
assert!(tasks.items.is_null()); assert!(tasks.items.is_null());
assert_eq!(tasks.len, 0); assert_eq!(tasks.len, 0);
assert_eq!(tasks._capacity, 0); assert_eq!(tasks.capacity, 0);
} }
} }

View file

@ -1,7 +1,22 @@
use crate::traits::*; use crate::traits::*;
use crate::types::*; use crate::types::*;
#[ffizz_header::item]
#[ffizz(order = 500)]
/// ***** TCUda *****
///
/// TCUda contains the details of a UDA. /// TCUda contains the details of a UDA.
///
/// ```c
/// typedef struct TCUda {
/// // Namespace of the UDA. For legacy UDAs, this may have a NULL ptr field.
/// struct TCString ns;
/// // UDA key. Must not be NULL.
/// struct TCString key;
/// // Content of the UDA. Must not be NULL.
/// struct TCString value;
/// } TCUda;
/// ```
#[repr(C)] #[repr(C)]
#[derive(Default)] #[derive(Default)]
pub struct TCUda { pub struct TCUda {
@ -59,9 +74,25 @@ impl PassByValue for TCUda {
} }
} }
#[ffizz_header::item]
#[ffizz(order = 510)]
/// ***** TCUdaList *****
///
/// TCUdaList represents a list of UDAs. /// TCUdaList represents a list of UDAs.
/// ///
/// The content of this struct must be treated as read-only. /// The content of this struct must be treated as read-only.
///
/// ```c
/// typedef struct TCUdaList {
/// // number of UDAs in items
/// size_t len;
/// // reserved
/// size_t _u1;
/// // Array of UDAs. These remain owned by the TCUdaList instance and will be freed by
/// // tc_uda_list_free. This pointer is never NULL for a valid TCUdaList.
/// struct TCUda *items;
/// } TCUdaList;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCUdaList { pub struct TCUdaList {
/// number of UDAs in items /// number of UDAs in items
@ -70,7 +101,7 @@ pub struct TCUdaList {
/// total size of items (internal use only) /// total size of items (internal use only)
_capacity: libc::size_t, _capacity: libc::size_t,
/// array of UDAs. These remain owned by the TCUdaList instance and will be freed by /// Array of UDAs. These remain owned by the TCUdaList instance and will be freed by
/// tc_uda_list_free. This pointer is never NULL for a valid TCUdaList. /// tc_uda_list_free. This pointer is never NULL for a valid TCUdaList.
items: *mut TCUda, items: *mut TCUda,
} }
@ -100,8 +131,14 @@ impl CList for TCUdaList {
} }
} }
#[ffizz_header::item]
#[ffizz(order = 501)]
/// Free a TCUda instance. The instance, and the TCStrings it contains, must not be used /// Free a TCUda instance. The instance, and the TCStrings it contains, must not be used
/// after this call. /// after this call.
///
/// ```c
/// EXTERN_C void tc_uda_free(struct TCUda *tcuda);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_uda_free(tcuda: *mut TCUda) { pub unsafe extern "C" fn tc_uda_free(tcuda: *mut TCUda) {
debug_assert!(!tcuda.is_null()); debug_assert!(!tcuda.is_null());
@ -111,10 +148,16 @@ pub unsafe extern "C" fn tc_uda_free(tcuda: *mut TCUda) {
drop(uda); drop(uda);
} }
#[ffizz_header::item]
#[ffizz(order = 511)]
/// Free a TCUdaList instance. The instance, and all TCUdas it contains, must not be used after /// Free a TCUdaList instance. The instance, and all TCUdas it contains, must not be used after
/// this call. /// this call.
/// ///
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUdaList. /// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUdaList.
///
/// ```c
/// EXTERN_C void tc_uda_list_free(struct TCUdaList *tcudas);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_uda_list_free(tcudas: *mut TCUdaList) { pub unsafe extern "C" fn tc_uda_list_free(tcudas: *mut TCUdaList) {
// SAFETY: // SAFETY:

View file

@ -3,14 +3,18 @@ use crate::types::*;
use libc; use libc;
use taskchampion::Uuid; use taskchampion::Uuid;
// NOTE: this must be a simple constant so that cbindgen can evaluate it #[ffizz_header::item]
/// Length, in bytes, of the string representation of a UUID (without NUL terminator) #[ffizz(order = 300)]
pub const TC_UUID_STRING_BYTES: usize = 36; /// ***** TCUuid *****
///
/// TCUuid is used as a task identifier. Uuids do not contain any pointers and need not be freed. /// 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. /// Uuids are typically treated as opaque, but the bytes are available in big-endian format.
/// ///
/// cbindgen:field-names=[bytes] /// ```c
/// typedef struct TCUuid {
/// uint8_t bytes[16];
/// } TCUuid;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCUuid([u8; 16]); pub struct TCUuid([u8; 16]);
@ -28,35 +32,41 @@ impl PassByValue for TCUuid {
} }
} }
/// Create a new, randomly-generated UUID. #[ffizz_header::item]
#[no_mangle] #[ffizz(order = 301)]
pub unsafe extern "C" fn tc_uuid_new_v4() -> TCUuid { /// Length, in bytes, of the string representation of a UUID (without NUL terminator)
// SAFETY: ///
// - value is not allocated /// ```c
unsafe { TCUuid::return_val(Uuid::new_v4()) } /// #define TC_UUID_STRING_BYTES 36
} /// ```
// TODO: debug_assert or static_assert this somewhere?
/// Create a new UUID with the nil value. pub const TC_UUID_STRING_BYTES: usize = 36;
#[no_mangle]
pub unsafe extern "C" fn tc_uuid_nil() -> TCUuid {
// SAFETY:
// - value is not allocated
unsafe { TCUuid::return_val(Uuid::nil()) }
}
#[ffizz_header::item]
#[ffizz(order = 310)]
/// TCUuidList represents a list of uuids. /// TCUuidList represents a list of uuids.
/// ///
/// The content of this struct must be treated as read-only. /// The content of this struct must be treated as read-only.
///
/// ```c
/// typedef struct TCUuidList {
/// // number of uuids in items
/// size_t len;
/// // reserved
/// size_t _u1;
/// // Array of uuids. This pointer is never NULL for a valid TCUuidList.
/// struct TCUuid *items;
/// } TCUuidList;
/// ```
#[repr(C)] #[repr(C)]
pub struct TCUuidList { pub struct TCUuidList {
/// number of uuids in items /// number of uuids in items
len: libc::size_t, len: libc::size_t,
/// total size of items (internal use only) /// total size of items (internal use only)
_capacity: libc::size_t, capacity: libc::size_t,
/// array of uuids. these remain owned by the TCUuidList instance and will be freed by /// Array of uuids. This pointer is never NULL for a valid TCUuidList.
/// tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList.
items: *mut TCUuid, items: *mut TCUuid,
} }
@ -66,7 +76,7 @@ impl CList for TCUuidList {
unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
TCUuidList { TCUuidList {
len, len,
_capacity: cap, capacity: cap,
items, items,
} }
} }
@ -81,12 +91,46 @@ impl CList for TCUuidList {
} }
fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
(self.items, self.len, self._capacity) (self.items, self.len, self.capacity)
} }
} }
#[ffizz_header::item]
#[ffizz(order = 302)]
/// Create a new, randomly-generated UUID.
///
/// ```c
/// EXTERN_C struct TCUuid tc_uuid_new_v4(void);
/// ```
#[no_mangle]
pub unsafe extern "C" fn tc_uuid_new_v4() -> TCUuid {
// SAFETY:
// - value is not allocated
unsafe { TCUuid::return_val(Uuid::new_v4()) }
}
#[ffizz_header::item]
#[ffizz(order = 302)]
/// Create a new UUID with the nil value.
///
/// ```c
/// EXTERN_C struct TCUuid tc_uuid_nil(void);
/// ```
#[no_mangle]
pub unsafe extern "C" fn tc_uuid_nil() -> TCUuid {
// SAFETY:
// - value is not allocated
unsafe { TCUuid::return_val(Uuid::nil()) }
}
#[ffizz_header::item]
#[ffizz(order = 302)]
/// 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.
///
/// ```c
/// EXTERN_C void tc_uuid_to_buf(struct TCUuid tcuuid, char *buf);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_uuid_to_buf(tcuuid: TCUuid, buf: *mut libc::c_char) { pub unsafe extern "C" fn tc_uuid_to_buf(tcuuid: TCUuid, buf: *mut libc::c_char) {
debug_assert!(!buf.is_null()); debug_assert!(!buf.is_null());
@ -104,8 +148,14 @@ pub unsafe extern "C" fn tc_uuid_to_buf(tcuuid: TCUuid, buf: *mut libc::c_char)
uuid.as_hyphenated().encode_lower(buf); uuid.as_hyphenated().encode_lower(buf);
} }
#[ffizz_header::item]
#[ffizz(order = 302)]
/// Return the hyphenated string representation of a TCUuid. The returned string /// Return the hyphenated string representation of a TCUuid. The returned string
/// must be freed with tc_string_free. /// must be freed with tc_string_free.
///
/// ```c
/// EXTERN_C struct TCString tc_uuid_to_str(struct TCUuid tcuuid);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_uuid_to_str(tcuuid: TCUuid) -> TCString { pub unsafe extern "C" fn tc_uuid_to_str(tcuuid: TCUuid) -> TCString {
// SAFETY: // SAFETY:
@ -117,8 +167,14 @@ pub unsafe extern "C" fn tc_uuid_to_str(tcuuid: TCUuid) -> TCString {
unsafe { TCString::return_val(s.into()) } unsafe { TCString::return_val(s.into()) }
} }
#[ffizz_header::item]
#[ffizz(order = 302)]
/// Parse the given string as a UUID. Returns TC_RESULT_ERROR on parse failure or if the given /// Parse the given string as a UUID. Returns TC_RESULT_ERROR on parse failure or if the given
/// string is not valid. /// string is not valid.
///
/// ```c
/// EXTERN_C TCResult tc_uuid_from_str(struct TCString s, struct TCUuid *uuid_out);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_uuid_from_str(s: TCString, uuid_out: *mut TCUuid) -> TCResult { pub unsafe extern "C" fn tc_uuid_from_str(s: TCString, uuid_out: *mut TCUuid) -> TCResult {
debug_assert!(!s.is_null()); debug_assert!(!s.is_null());
@ -139,10 +195,16 @@ pub unsafe extern "C" fn tc_uuid_from_str(s: TCString, uuid_out: *mut TCUuid) ->
TCResult::Error TCResult::Error
} }
#[ffizz_header::item]
#[ffizz(order = 312)]
/// Free a TCUuidList instance. The instance, and all TCUuids it contains, must not be used after /// Free a TCUuidList instance. The instance, and all TCUuids it contains, must not be used after
/// this call. /// this call.
/// ///
/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUuidList. /// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUuidList.
///
/// ```c
/// EXTERN_C void tc_uuid_list_free(struct TCUuidList *tcuuids);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_uuid_list_free(tcuuids: *mut TCUuidList) { pub unsafe extern "C" fn tc_uuid_list_free(tcuuids: *mut TCUuidList) {
// SAFETY: // SAFETY:
@ -161,7 +223,7 @@ mod test {
let tcuuids = unsafe { TCUuidList::return_val(Vec::new()) }; let tcuuids = unsafe { TCUuidList::return_val(Vec::new()) };
assert!(!tcuuids.items.is_null()); assert!(!tcuuids.items.is_null());
assert_eq!(tcuuids.len, 0); assert_eq!(tcuuids.len, 0);
assert_eq!(tcuuids._capacity, 0); assert_eq!(tcuuids.capacity, 0);
} }
#[test] #[test]
@ -171,6 +233,6 @@ mod test {
unsafe { tc_uuid_list_free(&mut tcuuids) }; unsafe { tc_uuid_list_free(&mut tcuuids) };
assert!(tcuuids.items.is_null()); assert!(tcuuids.items.is_null());
assert_eq!(tcuuids.len, 0); assert_eq!(tcuuids.len, 0);
assert_eq!(tcuuids._capacity, 0); assert_eq!(tcuuids.capacity, 0);
} }
} }

View file

@ -2,6 +2,10 @@ use crate::traits::*;
use crate::types::*; use crate::types::*;
use taskchampion::{Uuid, WorkingSet}; use taskchampion::{Uuid, WorkingSet};
#[ffizz_header::item]
#[ffizz(order = 1100)]
/// ***** TCWorkingSet *****
///
/// A TCWorkingSet represents a snapshot of the working set for a replica. It is not automatically /// A TCWorkingSet represents a snapshot of the working set for a replica. It is not automatically
/// updated based on changes in the replica. Its lifetime is independent of the replica and it can /// updated based on changes in the replica. Its lifetime is independent of the replica and it can
/// be freed at any time. /// be freed at any time.
@ -23,6 +27,10 @@ use taskchampion::{Uuid, WorkingSet};
/// Once passed to `tc_replica_free`, a `*TCWorkingSet` becomes invalid and must not be used again. /// Once passed to `tc_replica_free`, a `*TCWorkingSet` becomes invalid and must not be used again.
/// ///
/// TCWorkingSet is not threadsafe. /// TCWorkingSet is not threadsafe.
///
/// ```c
/// typedef struct TCWorkingSet TCWorkingSet;
/// ```
pub struct TCWorkingSet(WorkingSet); pub struct TCWorkingSet(WorkingSet);
impl PassByPointer for TCWorkingSet {} impl PassByPointer for TCWorkingSet {}
@ -45,20 +53,38 @@ where
f(&tcws.0) f(&tcws.0)
} }
#[ffizz_header::item]
#[ffizz(order = 1101)]
/// Get the working set's length, or the number of UUIDs it contains. /// Get the working set's length, or the number of UUIDs it contains.
///
/// ```c
/// EXTERN_C size_t tc_working_set_len(struct TCWorkingSet *ws);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_working_set_len(ws: *mut TCWorkingSet) -> usize { pub unsafe extern "C" fn tc_working_set_len(ws: *mut TCWorkingSet) -> usize {
wrap(ws, |ws| ws.len()) wrap(ws, |ws| ws.len())
} }
#[ffizz_header::item]
#[ffizz(order = 1101)]
/// Get the working set's largest index. /// Get the working set's largest index.
///
/// ```c
/// EXTERN_C size_t tc_working_set_largest_index(struct TCWorkingSet *ws);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_working_set_largest_index(ws: *mut TCWorkingSet) -> usize { pub unsafe extern "C" fn tc_working_set_largest_index(ws: *mut TCWorkingSet) -> usize {
wrap(ws, |ws| ws.largest_index()) wrap(ws, |ws| ws.largest_index())
} }
#[ffizz_header::item]
#[ffizz(order = 1101)]
/// Get the UUID for the task at the given index. Returns true if the UUID exists in the working /// Get the UUID for the task at the given index. Returns true if the UUID exists in the working
/// set. If not, returns false and does not change uuid_out. /// set. If not, returns false and does not change uuid_out.
///
/// ```c
/// EXTERN_C bool tc_working_set_by_index(struct TCWorkingSet *ws, size_t index, struct TCUuid *uuid_out);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_working_set_by_index( pub unsafe extern "C" fn tc_working_set_by_index(
ws: *mut TCWorkingSet, ws: *mut TCWorkingSet,
@ -79,8 +105,14 @@ pub unsafe extern "C" fn tc_working_set_by_index(
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1101)]
/// Get the working set index for the task with the given UUID. Returns 0 if the task is not in /// Get the working set index for the task with the given UUID. Returns 0 if the task is not in
/// the working set. /// the working set.
///
/// ```c
/// EXTERN_C size_t tc_working_set_by_uuid(struct TCWorkingSet *ws, struct TCUuid uuid);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_working_set_by_uuid(ws: *mut TCWorkingSet, uuid: TCUuid) -> usize { pub unsafe extern "C" fn tc_working_set_by_uuid(ws: *mut TCWorkingSet, uuid: TCUuid) -> usize {
wrap(ws, |ws| { wrap(ws, |ws| {
@ -91,8 +123,14 @@ pub unsafe extern "C" fn tc_working_set_by_uuid(ws: *mut TCWorkingSet, uuid: TCU
}) })
} }
#[ffizz_header::item]
#[ffizz(order = 1102)]
/// Free a TCWorkingSet. The given value must not be NULL. The value must not be used after this /// Free a TCWorkingSet. The given value must not be NULL. The value must not be used after this
/// function returns, and must not be freed more than once. /// function returns, and must not be freed more than once.
///
/// ```c
/// EXTERN_C void tc_working_set_free(struct TCWorkingSet *ws);
/// ```
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_working_set_free(ws: *mut TCWorkingSet) { pub unsafe extern "C" fn tc_working_set_free(ws: *mut TCWorkingSet) {
// SAFETY: // SAFETY:

File diff suppressed because it is too large Load diff

View file

@ -5,4 +5,4 @@ edition = "2018"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
cbindgen = "0.24.3" taskchampion-lib = { path = "../lib" }

View file

@ -3,8 +3,9 @@
//! At the moment it is very simple, but if this grows more subcommands then //! At the moment it is very simple, but if this grows more subcommands then
//! it will be sensible to use `clap` or another similar library. //! it will be sensible to use `clap` or another similar library.
use cbindgen::*;
use std::env; use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
pub fn main() -> anyhow::Result<()> { pub fn main() -> anyhow::Result<()> {
@ -18,32 +19,13 @@ pub fn main() -> anyhow::Result<()> {
/// `cargo xtask codegen` /// `cargo xtask codegen`
/// ///
/// This uses cbindgen to generate `lib/taskchampion.h`. /// This uses ffizz-header to generate `lib/taskchampion.h`.
fn codegen() -> anyhow::Result<()> { fn codegen() -> anyhow::Result<()> {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let workspace_dir = manifest_dir.parent().unwrap(); let workspace_dir = manifest_dir.parent().unwrap();
let lib_crate_dir = workspace_dir.join("lib"); let lib_crate_dir = workspace_dir.join("lib");
let mut file = File::create(lib_crate_dir.join("taskchampion.h")).unwrap();
Builder::new() write!(&mut file, "{}", ::taskchampion_lib::generate_header()).unwrap();
.with_crate(&lib_crate_dir)
.with_config(Config {
header: Some(include_str!("../../lib/header-intro.h").into()),
language: Language::C,
include_guard: Some("TASKCHAMPION_H".into()),
cpp_compat: true,
sys_includes: vec!["stdbool.h".into(), "stdint.h".into(), "time.h".into()],
usize_is_size_t: true,
no_includes: true,
enumeration: EnumConfig {
// this appears to still default to true for C
enum_class: false,
..Default::default()
},
..Default::default()
})
.generate()
.expect("Unable to generate bindings")
.write_to_file(lib_crate_dir.join("taskchampion.h"));
Ok(()) Ok(())
} }