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}