refactor annotations to handle invalid strings

This commit is contained in:
Dustin J. Mitchell 2022-02-12 00:26:57 +00:00
parent 7ebdaa761c
commit 76cbc2880b
2 changed files with 19 additions and 16 deletions

View file

@ -1,6 +1,6 @@
use crate::traits::*; use crate::traits::*;
use crate::types::*; use crate::types::*;
use taskchampion::Annotation; use chrono::prelude::*;
/// TCAnnotation contains the details of an annotation. /// TCAnnotation contains the details of an annotation.
#[repr(C)] #[repr(C)]
@ -12,9 +12,11 @@ pub struct TCAnnotation {
} }
impl PassByValue for TCAnnotation { impl PassByValue for TCAnnotation {
type RustType = Annotation; // NOTE: we cannot use `RustType = Annotation` here because conversion of the
// TCString to a String can fail.
type RustType = (DateTime<Utc>, TCString<'static>);
unsafe fn from_ctype(self) -> Annotation { unsafe fn from_ctype(self) -> Self::RustType {
// SAFETY: // SAFETY:
// - any time_t value is valid // - any time_t value is valid
// - time_t is not zero, so unwrap is safe (see type docstring) // - time_t is not zero, so unwrap is safe (see type docstring)
@ -22,18 +24,14 @@ impl PassByValue for TCAnnotation {
// SAFETY: // SAFETY:
// - self is owned, so we can take ownership of this TCString // - self is owned, so we can take ownership of this TCString
// - self.description is a valid, non-null TCString (see type docstring) // - self.description is a valid, non-null TCString (see type docstring)
let description = unsafe { TCString::take_from_arg(self.description) } let description = unsafe { TCString::take_from_arg(self.description) };
.into_string() (entry, description)
// TODO: might not be valid utf-8
.unwrap();
Annotation { entry, description }
} }
fn as_ctype(arg: Annotation) -> Self { fn as_ctype((entry, description): Self::RustType) -> Self {
let description: TCString = arg.description.into();
TCAnnotation { TCAnnotation {
entry: libc::time_t::as_ctype(Some(arg.entry)), entry: libc::time_t::as_ctype(Some(entry)),
// SAFETY: caller will later free this value via tc_annotation_free // SAFETY: caller assumes ownership of this value
description: unsafe { description.return_val() }, description: unsafe { description.return_val() },
} }
} }

View file

@ -6,7 +6,7 @@ use std::convert::TryFrom;
use std::ops::Deref; use std::ops::Deref;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::str::FromStr; use std::str::FromStr;
use taskchampion::{Tag, Task, TaskMut}; use taskchampion::{Annotation, Tag, Task, TaskMut};
/// A task, as publicly exposed by this library. /// A task, as publicly exposed by this library.
/// ///
@ -331,7 +331,10 @@ pub unsafe extern "C" fn tc_task_get_annotations<'a>(task: *mut TCTask) -> TCAnn
wrap(task, |task| { wrap(task, |task| {
let vec: Vec<TCAnnotation> = task let vec: Vec<TCAnnotation> = task
.get_annotations() .get_annotations()
.map(|a| TCAnnotation::as_ctype(a)) .map(|a| {
let description = TCString::from(a.description);
TCAnnotation::as_ctype((a.entry, description))
})
.collect(); .collect();
TCAnnotationList::return_val(vec) TCAnnotationList::return_val(vec)
}) })
@ -513,11 +516,13 @@ pub unsafe extern "C" fn tc_task_add_annotation(
annotation: *mut TCAnnotation, annotation: *mut TCAnnotation,
) -> TCResult { ) -> TCResult {
// SAFETY: see TCAnnotation docstring // SAFETY: see TCAnnotation docstring
let ann = unsafe { TCAnnotation::take_from_arg(annotation, TCAnnotation::default()) }; let (entry, description) =
unsafe { TCAnnotation::take_from_arg(annotation, TCAnnotation::default()) };
wrap_mut( wrap_mut(
task, task,
|task| { |task| {
task.add_annotation(ann)?; let description = description.into_string()?;
task.add_annotation(Annotation { entry, description })?;
Ok(TCResult::Ok) Ok(TCResult::Ok)
}, },
TCResult::Error, TCResult::Error,