taskchampion_lib/
annotation.rs

1use crate::traits::*;
2use crate::types::*;
3use taskchampion::chrono::prelude::*;
4
5#[ffizz_header::item]
6#[ffizz(order = 400)]
7/// ***** TCAnnotation *****
8///
9/// TCAnnotation contains the details of an annotation.
10///
11/// # Safety
12///
13/// An annotation must be initialized from a tc_.. function, and later freed
14/// with `tc_annotation_free` or `tc_annotation_list_free`.
15///
16/// Any function taking a `*TCAnnotation` requires:
17///  - the pointer must not be NUL;
18///  - the pointer must be one previously returned from a tc_… function;
19///  - the memory referenced by the pointer must never be modified by C code; and
20///  - ownership transfers to the called function, and the value must not be used
21///    after the call returns.  In fact, the value will be zeroed out to ensure this.
22///
23/// TCAnnotations are not threadsafe.
24///
25/// ```c
26/// typedef struct TCAnnotation {
27///   // Time the annotation was made.  Must be nonzero.
28///   time_t entry;
29///   // Content of the annotation.  Must not be NULL.
30///   TCString description;
31/// } TCAnnotation;
32/// ```
33#[repr(C)]
34pub struct TCAnnotation {
35    pub entry: libc::time_t,
36    pub description: TCString,
37}
38
39impl PassByValue for TCAnnotation {
40    // NOTE: we cannot use `RustType = Annotation` here because conversion of the
41    // Rust to a String can fail.
42    type RustType = (DateTime<Utc>, RustString<'static>);
43
44    unsafe fn from_ctype(mut self) -> Self::RustType {
45        // SAFETY:
46        //  - any time_t value is valid
47        //  - time_t is copy, so ownership is not important
48        let entry = unsafe { libc::time_t::val_from_arg(self.entry) }.unwrap();
49        // SAFETY:
50        //  - self.description is valid (came from return_val in as_ctype)
51        //  - self is owned, so we can take ownership of this TCString
52        let description =
53            unsafe { TCString::take_val_from_arg(&mut self.description, TCString::default()) };
54        (entry, description)
55    }
56
57    fn as_ctype((entry, description): Self::RustType) -> Self {
58        TCAnnotation {
59            entry: libc::time_t::as_ctype(Some(entry)),
60            // SAFETY:
61            //  - ownership of the TCString tied to ownership of Self
62            description: unsafe { TCString::return_val(description) },
63        }
64    }
65}
66
67impl Default for TCAnnotation {
68    fn default() -> Self {
69        TCAnnotation {
70            entry: 0 as libc::time_t,
71            description: TCString::default(),
72        }
73    }
74}
75
76#[ffizz_header::item]
77#[ffizz(order = 410)]
78/// ***** TCAnnotationList *****
79///
80/// TCAnnotationList represents a list of annotations.
81///
82/// The content of this struct must be treated as read-only.
83///
84/// ```c
85/// typedef struct TCAnnotationList {
86///   // number of annotations in items
87///   size_t len;
88///   // reserved
89///   size_t _u1;
90///   // Array of annotations. These remain owned by the TCAnnotationList instance and will be freed by
91///   // tc_annotation_list_free.  This pointer is never NULL for a valid TCAnnotationList.
92///   struct TCAnnotation *items;
93/// } TCAnnotationList;
94/// ```
95#[repr(C)]
96pub struct TCAnnotationList {
97    len: libc::size_t,
98    /// total size of items (internal use only)
99    capacity: libc::size_t,
100    items: *mut TCAnnotation,
101}
102
103impl CList for TCAnnotationList {
104    type Element = TCAnnotation;
105
106    unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
107        TCAnnotationList {
108            len,
109            capacity: cap,
110            items,
111        }
112    }
113
114    fn slice(&mut self) -> &mut [Self::Element] {
115        // SAFETY:
116        //  - because we have &mut self, we have read/write access to items[0..len]
117        //  - all items are properly initialized Element's
118        //  - return value lifetime is equal to &mmut self's, so access is exclusive
119        //  - items and len came from Vec, so total size is < isize::MAX
120        unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
121    }
122
123    fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
124        (self.items, self.len, self.capacity)
125    }
126}
127
128#[ffizz_header::item]
129#[ffizz(order = 401)]
130/// Free a TCAnnotation instance.  The instance, and the TCString it contains, must not be used
131/// after this call.
132///
133/// ```c
134/// EXTERN_C void tc_annotation_free(struct TCAnnotation *tcann);
135/// ```
136#[no_mangle]
137pub unsafe extern "C" fn tc_annotation_free(tcann: *mut TCAnnotation) {
138    debug_assert!(!tcann.is_null());
139    // SAFETY:
140    //  - tcann is not NULL
141    //  - *tcann is a valid TCAnnotation (caller promised to treat it as read-only)
142    let annotation = unsafe { TCAnnotation::take_val_from_arg(tcann, TCAnnotation::default()) };
143    drop(annotation);
144}
145
146#[ffizz_header::item]
147#[ffizz(order = 411)]
148/// Free a TCAnnotationList instance.  The instance, and all TCAnnotations it contains, must not be used after
149/// this call.
150///
151/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCAnnotationList.
152///
153/// ```c
154/// EXTERN_C void tc_annotation_list_free(struct TCAnnotationList *tcanns);
155/// ```
156#[no_mangle]
157pub unsafe extern "C" fn tc_annotation_list_free(tcanns: *mut TCAnnotationList) {
158    // SAFETY:
159    //  - tcanns is not NULL and points to a valid TCAnnotationList (caller is not allowed to
160    //    modify the list)
161    //  - caller promises not to use the value after return
162    unsafe { drop_value_list(tcanns) }
163}
164
165#[cfg(test)]
166mod test {
167    use super::*;
168
169    #[test]
170    fn empty_list_has_non_null_pointer() {
171        let tcanns = unsafe { TCAnnotationList::return_val(Vec::new()) };
172        assert!(!tcanns.items.is_null());
173        assert_eq!(tcanns.len, 0);
174        assert_eq!(tcanns.capacity, 0);
175    }
176
177    #[test]
178    fn free_sets_null_pointer() {
179        let mut tcanns = unsafe { TCAnnotationList::return_val(Vec::new()) };
180        // SAFETY: testing expected behavior
181        unsafe { tc_annotation_list_free(&mut tcanns) };
182        assert!(tcanns.items.is_null());
183        assert_eq!(tcanns.len, 0);
184        assert_eq!(tcanns.capacity, 0);
185    }
186}