use std::fmt;
use std::result::Result as RResult;
use chrono::Utc;
use serde::de::{Error, MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Deserializer};
use serde::{Serialize, Serializer};
use uuid::Uuid;
use crate::annotation::Annotation;
use crate::date::Date;
use crate::priority::TaskPriority;
use crate::project::Project;
use crate::status::TaskStatus;
use crate::tag::Tag;
use crate::uda::{UDAName, UDAValue, UDA};
use crate::urgency::Urgency;
#[derive(Debug, Clone, PartialEq, derive_builder::Builder)]
#[builder(setter(into))]
pub struct Task {
#[builder(default)]
id: Option<u64>,
#[builder(default = "TaskStatus::Pending")]
status: TaskStatus,
#[builder(default = "Uuid::new_v4()")]
uuid: Uuid,
#[builder(default = "Date::from(Utc::now().naive_utc())")]
entry: Date,
description: String,
#[builder(default)]
annotations: Option<Vec<Annotation>>,
#[builder(default)]
depends: Option<Vec<Uuid>>,
#[builder(default)]
due: Option<Date>,
#[builder(default)]
end: Option<Date>,
#[builder(default)]
imask: Option<f64>,
#[builder(default)]
mask: Option<String>,
#[builder(default)]
modified: Option<Date>,
#[builder(default)]
parent: Option<Uuid>,
#[builder(default)]
priority: Option<TaskPriority>,
#[builder(default)]
project: Option<Project>,
#[builder(default)]
recur: Option<String>,
#[builder(default)]
scheduled: Option<Date>,
#[builder(default)]
start: Option<Date>,
#[builder(default)]
tags: Option<Vec<Tag>>,
#[builder(default)]
until: Option<Date>,
#[builder(default)]
wait: Option<Date>,
#[builder(default)]
urgency: Option<Urgency>,
#[builder(default)]
uda: UDA,
}
impl Task {
#[allow(clippy::too_many_arguments)]
pub fn new(
id: Option<u64>,
status: TaskStatus,
uuid: Uuid,
entry: Date,
description: String,
annotations: Option<Vec<Annotation>>,
depends: Option<Vec<Uuid>>,
due: Option<Date>,
end: Option<Date>,
imask: Option<f64>,
mask: Option<String>,
modified: Option<Date>,
parent: Option<Uuid>,
priority: Option<TaskPriority>,
project: Option<Project>,
recur: Option<String>,
scheduled: Option<Date>,
start: Option<Date>,
tags: Option<Vec<Tag>>,
until: Option<Date>,
wait: Option<Date>,
urgency: Option<Urgency>,
uda: UDA,
) -> Task {
Task {
id,
status,
uuid,
entry,
description,
annotations,
depends,
due,
end,
imask,
mask,
modified,
parent,
priority,
project,
recur,
scheduled,
start,
tags,
until,
wait,
urgency,
uda,
}
}
pub fn id(&self) -> Option<u64> {
self.id
}
pub fn status(&self) -> &TaskStatus {
&self.status
}
pub fn status_mut(&mut self) -> &mut TaskStatus {
&mut self.status
}
pub fn uuid(&self) -> &Uuid {
&self.uuid
}
pub fn uuid_mut(&mut self) -> &mut Uuid {
&mut self.uuid
}
pub fn entry(&self) -> &Date {
&self.entry
}
pub fn entry_mut(&mut self) -> &mut Date {
&mut self.entry
}
pub fn description(&self) -> &String {
&self.description
}
pub fn description_mut(&mut self) -> &mut String {
&mut self.description
}
pub fn annotations(&self) -> Option<&Vec<Annotation>> {
self.annotations.as_ref()
}
pub fn annotations_mut(&mut self) -> Option<&mut Vec<Annotation>> {
self.annotations.as_mut()
}
pub fn set_annotations<T, A>(&mut self, new: Option<T>)
where
T: IntoIterator,
T::Item: Into<Annotation>,
{
self.annotations = new.map(|x| x.into_iter().map(Into::into).collect());
}
pub fn depends(&self) -> Option<&Vec<Uuid>> {
self.depends.as_ref()
}
pub fn depends_mut(&mut self) -> Option<&mut Vec<Uuid>> {
self.depends.as_mut()
}
pub fn set_depends<T, U>(&mut self, new: Option<T>)
where
T: IntoIterator,
T::Item: Into<Uuid>,
{
self.depends = new.map(|x| x.into_iter().map(Into::into).collect());
}
pub fn due(&self) -> Option<&Date> {
self.due.as_ref()
}
pub fn due_mut(&mut self) -> Option<&mut Date> {
self.due.as_mut()
}
pub fn set_due<T>(&mut self, new: Option<T>)
where
T: Into<Date>,
{
self.due = new.map(Into::into)
}
pub fn end(&self) -> Option<&Date> {
self.end.as_ref()
}
pub fn end_mut(&mut self) -> Option<&mut Date> {
self.end.as_mut()
}
pub fn set_end<T>(&mut self, new: Option<T>)
where
T: Into<Date>,
{
self.end = new.map(Into::into)
}
pub fn imask(&self) -> Option<&f64> {
self.imask.as_ref()
}
pub fn imask_mut(&mut self) -> Option<&mut f64> {
self.imask.as_mut()
}
pub fn set_imask<T>(&mut self, new: Option<T>)
where
T: Into<f64>,
{
self.imask = new.map(Into::into)
}
pub fn mask(&self) -> Option<&String> {
self.mask.as_ref()
}
pub fn mask_mut(&mut self) -> Option<&mut String> {
self.mask.as_mut()
}
pub fn set_mask<T>(&mut self, new: Option<T>)
where
T: Into<String>,
{
self.mask = new.map(Into::into)
}
pub fn modified(&self) -> Option<&Date> {
self.modified.as_ref()
}
pub fn modified_mut(&mut self) -> Option<&mut Date> {
self.modified.as_mut()
}
pub fn set_modified<T>(&mut self, new: Option<T>)
where
T: Into<Date>,
{
self.modified = new.map(Into::into)
}
pub fn parent(&self) -> Option<&Uuid> {
self.parent.as_ref()
}
pub fn parent_mut(&mut self) -> Option<&mut Uuid> {
self.parent.as_mut()
}
pub fn set_parent<T>(&mut self, new: Option<T>)
where
T: Into<Uuid>,
{
self.parent = new.map(Into::into)
}
pub fn priority(&self) -> Option<&TaskPriority> {
self.priority.as_ref()
}
pub fn priority_mut(&mut self) -> Option<&mut TaskPriority> {
self.priority.as_mut()
}
pub fn set_priority<T>(&mut self, new: Option<T>)
where
T: Into<TaskPriority>,
{
self.priority = new.map(Into::into)
}
pub fn project(&self) -> Option<&Project> {
self.project.as_ref()
}
pub fn project_mut(&mut self) -> Option<&mut Project> {
self.project.as_mut()
}
pub fn set_project<T>(&mut self, new: Option<T>)
where
T: Into<Project>,
{
self.project = new.map(Into::into)
}
pub fn recur(&self) -> Option<&String> {
self.recur.as_ref()
}
pub fn recur_mut(&mut self) -> Option<&mut String> {
self.recur.as_mut()
}
pub fn set_recur<T>(&mut self, new: Option<T>)
where
T: Into<String>,
{
self.recur = new.map(Into::into)
}
pub fn scheduled(&self) -> Option<&Date> {
self.scheduled.as_ref()
}
pub fn scheduled_mut(&mut self) -> Option<&mut Date> {
self.scheduled.as_mut()
}
pub fn set_scheduled<T>(&mut self, new: Option<T>)
where
T: Into<Date>,
{
self.scheduled = new.map(Into::into)
}
pub fn start(&self) -> Option<&Date> {
self.start.as_ref()
}
pub fn start_mut(&mut self) -> Option<&mut Date> {
self.start.as_mut()
}
pub fn set_start<T>(&mut self, new: Option<T>)
where
T: Into<Date>,
{
self.start = new.map(Into::into)
}
pub fn tags(&self) -> Option<&Vec<Tag>> {
self.tags.as_ref()
}
pub fn tags_mut(&mut self) -> Option<&mut Vec<Tag>> {
self.tags.as_mut()
}
pub fn set_tags<T>(&mut self, new: Option<T>)
where
T: IntoIterator,
T::Item: Into<Tag>,
{
self.tags = new.map(|x| x.into_iter().map(Into::into).collect());
}
pub fn until(&self) -> Option<&Date> {
self.until.as_ref()
}
pub fn until_mut(&mut self) -> Option<&mut Date> {
self.until.as_mut()
}
pub fn set_until<T>(&mut self, new: Option<T>)
where
T: Into<Date>,
{
self.until = new.map(Into::into);
}
pub fn urgency(&self) -> Option<&Urgency> {
self.urgency.as_ref()
}
pub fn urgency_mut(&mut self) -> Option<&mut Urgency> {
self.urgency.as_mut()
}
pub fn set_urgency<T>(&mut self, new: Option<T>)
where
T: Into<Urgency>,
{
self.urgency = new.map(Into::into);
}
pub fn wait(&self) -> Option<&Date> {
self.wait.as_ref()
}
pub fn wait_mut(&mut self) -> Option<&mut Date> {
self.wait.as_mut()
}
pub fn set_wait<T>(&mut self, new: Option<T>)
where
T: Into<Date>,
{
self.wait = new.map(Into::into);
}
pub fn uda(&self) -> &UDA {
&self.uda
}
pub fn uda_mut(&mut self) -> &mut UDA {
&mut self.uda
}
}
impl Serialize for Task {
fn serialize<S>(&self, serializer: S) -> RResult<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_map(None)?;
state.serialize_entry("status", &self.status)?;
state.serialize_entry("uuid", &self.uuid)?;
state.serialize_entry("entry", &self.entry)?;
state.serialize_entry("description", &self.description)?;
self.annotations
.as_ref()
.map(|v| state.serialize_entry("annotations", v));
self.tags.as_ref().map(|v| state.serialize_entry("tags", v));
self.id.as_ref().map(|v| state.serialize_entry("id", v));
self.recur
.as_ref()
.map(|ref v| state.serialize_entry("recur", v));
self.depends.as_ref().map(|v| {
let v: Vec<String> = v.iter().map(Uuid::to_string).collect();
state.serialize_entry("depends", &v.join(","))
});
self.due
.as_ref()
.map(|ref v| state.serialize_entry("due", v));
self.end
.as_ref()
.map(|ref v| state.serialize_entry("end", v));
self.imask
.as_ref()
.map(|ref v| state.serialize_entry("imask", v));
self.mask
.as_ref()
.map(|ref v| state.serialize_entry("mask", v));
self.modified
.as_ref()
.map(|ref v| state.serialize_entry("modified", v));
self.parent
.as_ref()
.map(|ref v| state.serialize_entry("parent", v));
self.priority
.as_ref()
.map(|ref v| state.serialize_entry("priority", v));
self.project
.as_ref()
.map(|ref v| state.serialize_entry("project", v));
self.scheduled
.as_ref()
.map(|ref v| state.serialize_entry("scheduled", v));
self.start
.as_ref()
.map(|ref v| state.serialize_entry("start", v));
self.until
.as_ref()
.map(|ref v| state.serialize_entry("until", v));
self.wait
.as_ref()
.map(|ref v| state.serialize_entry("wait", v));
self.urgency
.as_ref()
.map(|ref v| state.serialize_entry("urgency", v));
for (key, value) in self.uda().iter() {
state.serialize_entry(key, value)?;
}
state.end()
}
}
impl<'de> Deserialize<'de> for Task {
fn deserialize<D>(deserializer: D) -> RResult<Task, D::Error>
where
D: Deserializer<'de>,
{
static FIELDS: &[&str] = &[
"id",
"status",
"uuid",
"entry",
"description",
"annotations",
"depends",
"due",
"end",
"imask",
"mask",
"modified",
"parent",
"priority",
"project",
"recur",
"scheduled",
"start",
"tags",
"until",
"wait",
"urgency",
"uda",
];
deserializer.deserialize_struct("Task", FIELDS, TaskDeserializeVisitor)
}
}
struct TaskDeserializeVisitor;
impl<'de> Visitor<'de> for TaskDeserializeVisitor {
type Value = Task;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "A dictionary containing task properties")
}
fn visit_map<V>(self, mut visitor: V) -> RResult<Task, V::Error>
where
V: MapAccess<'de>,
{
let mut id = None;
let mut status = None;
let mut uuid = None;
let mut entry = None;
let mut description = None;
let mut annotations = None;
let mut depends = None;
let mut due = None;
let mut end = None;
let mut imask = None;
let mut mask = None;
let mut modified = None;
let mut parent = None;
let mut priority = None;
let mut project = None;
let mut recur = None;
let mut scheduled = None;
let mut start = None;
let mut tags = None;
let mut until = None;
let mut wait = None;
let mut urgency = None;
let mut uda = UDA::default();
loop {
let key: Option<String> = visitor.next_key()?;
if key.is_none() {
break;
}
let key = key.unwrap();
match &key[..] {
"id" => {
id = Some(visitor.next_value()?);
}
"status" => {
status = Some(visitor.next_value()?);
}
"uuid" => {
uuid = Some(visitor.next_value()?);
}
"entry" => {
entry = Some(visitor.next_value()?);
}
"description" => {
description = Some(visitor.next_value()?);
}
"annotations" => {
annotations = Some(visitor.next_value()?);
}
"depends" => {
let raw: String = visitor.next_value()?;
let mut uuids = vec![];
for uuid in raw.split(',') {
uuids.push(Uuid::parse_str(uuid).map_err(V::Error::custom)?);
}
depends = Some(uuids);
}
"due" => {
due = Some(visitor.next_value()?);
}
"end" => {
end = Some(visitor.next_value()?);
}
"imask" => {
imask = Some(visitor.next_value()?);
}
"mask" => {
mask = Some(visitor.next_value()?);
}
"modified" => {
modified = Some(visitor.next_value()?);
}
"parent" => {
parent = Some(visitor.next_value()?);
}
"priority" => {
priority = Some(visitor.next_value()?);
}
"project" => {
project = Some(visitor.next_value()?);
}
"recur" => {
recur = Some(visitor.next_value()?);
}
"scheduled" => {
scheduled = Some(visitor.next_value()?);
}
"start" => {
start = Some(visitor.next_value()?);
}
"tags" => {
tags = Some(visitor.next_value()?);
}
"until" => {
until = Some(visitor.next_value()?);
}
"wait" => {
wait = Some(visitor.next_value()?);
}
"urgency" => {
urgency = Some(visitor.next_value()?);
}
field => {
log::debug!("Inserting '{}' as UDA", field);
let uda_value: UDAValue = visitor.next_value()?;
uda.insert(UDAName::from(field), uda_value);
}
}
}
let status = status.ok_or_else(|| V::Error::missing_field("status"))?;
let uuid = uuid.ok_or_else(|| V::Error::missing_field("uuid"))?;
let entry = entry.ok_or_else(|| V::Error::missing_field("entry"))?;
let description = description.ok_or_else(|| V::Error::missing_field("description"))?;
let task = Task::new(
id,
status,
uuid,
entry,
description,
annotations,
depends,
due,
end,
imask,
mask,
modified,
parent,
priority,
project,
recur,
scheduled,
start,
tags,
until,
wait,
urgency,
uda,
);
Ok(task)
}
}
#[cfg(test)]
mod test {
use crate::annotation::Annotation;
use crate::date::Date;
use crate::date::TASKWARRIOR_DATETIME_TEMPLATE;
use crate::status::TaskStatus;
use crate::task::Task;
use crate::uda::UDAValue;
use chrono::NaiveDateTime;
use serde_json;
use uuid::Uuid;
fn mklogger() {
let _ = env_logger::init();
log::debug!("Env-logger enabled");
}
fn mkdate(s: &str) -> Date {
let n = NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE);
Date::from(n.unwrap())
}
#[test]
fn test_deser() {
let s = r#"{
"id": 1,
"description": "test",
"entry": "20150619T165438Z",
"status": "waiting",
"uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
"urgency": 5.3
}"#;
println!("{}", s);
let task = serde_json::from_str(s);
println!("{:?}", task);
assert!(task.is_ok());
let task: Task = task.unwrap();
assert_eq!(*task.status(), TaskStatus::Waiting);
assert_eq!(task.description(), "test");
assert_eq!(*task.entry(), mkdate("20150619T165438Z"));
assert_eq!(
*task.uuid(),
Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap()
);
assert_eq!(task.urgency(), Some(&5.3));
let back = serde_json::to_string(&task).unwrap();
assert!(back.contains("description"));
assert!(back.contains("test"));
assert!(back.contains("entry"));
assert!(back.contains("20150619T165438Z"));
assert!(back.contains("status"));
assert!(back.contains("waiting"));
assert!(back.contains("uuid"));
assert!(back.contains("urgency"));
assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"));
}
#[test]
fn test_deser_more() {
mklogger();
let s = r#"{
"id": 1,
"description": "some description",
"entry": "20150619T165438Z",
"modified": "20160327T164007Z",
"project": "someproject",
"status": "waiting",
"tags": ["some", "tags", "are", "here"],
"uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
"depends": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0,5a04bb1e-3f4b-49fb-b9ba-44407ca223b5",
"wait": "20160508T164007Z",
"urgency": 0.583562
}"#;
println!("{}", s);
let task = serde_json::from_str(s);
println!("{:?}", task);
assert!(task.is_ok());
let task: Task = task.unwrap();
assert_eq!(*task.status(), TaskStatus::Waiting);
assert_eq!(task.description(), "some description");
assert_eq!(*task.entry(), mkdate("20150619T165438Z"));
assert_eq!(
*task.uuid(),
Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap()
);
assert_eq!(task.urgency(), Some(&0.583562));
assert_eq!(task.modified(), Some(&mkdate("20160327T164007Z")));
assert_eq!(task.project(), Some(&String::from("someproject")));
if let Some(tags) = task.tags() {
for tag in tags {
let any_tag = ["some", "tags", "are", "here"].iter().any(|t| tag == *t);
assert!(any_tag, "Tag {} missing", tag);
}
} else {
panic!("Tags completely missing");
}
assert_eq!(task.wait(), Some(&mkdate("20160508T164007Z")));
let back = serde_json::to_string(&task).unwrap();
assert!(back.contains("description"));
assert!(back.contains("some description"));
assert!(back.contains("entry"));
assert!(back.contains("20150619T165438Z"));
assert!(back.contains("project"));
assert!(back.contains("someproject"));
assert!(back.contains("status"));
assert!(back.contains("waiting"));
assert!(back.contains("tags"));
assert!(back.contains("some"));
assert!(back.contains("tags"));
assert!(back.contains("are"));
assert!(back.contains("here"));
assert!(back.contains("uuid"));
assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"));
assert!(
back.contains(
"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0,5a04bb1e-3f4b-49fb-b9ba-44407ca223b5",
)
);
}
#[test]
fn test_deser_annotation() {
let s = r#"{
"id":192,
"description":"Some long description for a task",
"entry":"20160423T125820Z",
"modified":"20160423T125942Z",
"project":"project",
"status":"pending",
"tags":["search","things"],
"uuid":"5a04bb1e-3f4b-49fb-b9ba-44407ca223b5",
"annotations":[{"entry":"20160423T125911Z","description":"An Annotation"},
{"entry":"20160423T125926Z","description":"Another Annotation"},
{"entry":"20160422T125942Z","description":"A Third Anno"}
],
"urgency": -5
}"#;
println!("{}", s);
let task = serde_json::from_str(s);
println!("{:?}", task);
assert!(task.is_ok());
let task: Task = task.unwrap();
assert_eq!(task.urgency(), Some(&-5.0));
let all_annotations = vec![
Annotation::new(mkdate("20160423T125911Z"), String::from("An Annotation")),
Annotation::new(
mkdate("20160423T125926Z"),
String::from("Another Annotation"),
),
Annotation::new(mkdate("20160422T125942Z"), String::from("A Third Anno")),
];
if let Some(annotations) = task.annotations() {
for annotation in annotations {
let r = all_annotations.iter().any(|ann| {
let descr = ann.description() == annotation.description();
let entry = ann.entry() == annotation.entry();
descr && entry
});
assert!(r, "Annotation {:?} missing or buggy", annotation);
}
} else {
panic!("Annotations missing");
}
}
#[test]
fn test_uda() {
let s = r#"{
"description":"Some long description for a task",
"entry":"20160423T125820Z",
"modified":"20160423T125942Z",
"project":"project",
"status":"pending",
"uuid":"5a04bb1e-3f4b-49fb-b9ba-44407ca223b5",
"test_str_uda":"test_str_uda_value",
"test_float_uda":-17.1234,
"test_int_uda":1234
}"#;
println!("{}", s);
let task = serde_json::from_str(s);
println!("{:?}", task);
assert!(task.is_ok());
let task: Task = task.unwrap();
let str_uda = task.uda().get(&"test_str_uda".to_owned());
assert!(str_uda.is_some());
let str_uda = str_uda.unwrap();
assert_eq!(str_uda, &UDAValue::Str("test_str_uda_value".to_owned()));
let float_uda = task.uda().get(&"test_float_uda".to_owned());
assert!(float_uda.is_some());
let float_uda = float_uda.unwrap();
assert_eq!(float_uda, &UDAValue::F64(-17.1234));
let int_uda = task.uda().get(&"test_int_uda".to_owned());
assert!(int_uda.is_some());
let int_uda = int_uda.unwrap();
assert_eq!(int_uda, &UDAValue::U64(1234));
let back = serde_json::to_string_pretty(&task);
assert!(back.is_ok());
let back = back.unwrap();
println!("{}", back);
assert!(back.contains("description"));
assert!(back.contains("Some long description for a task"));
assert!(back.contains("entry"));
assert!(back.contains("20160423T125820Z"));
assert!(back.contains("project"));
assert!(back.contains("status"));
assert!(back.contains("pending"));
assert!(back.contains("uuid"));
assert!(back.contains("5a04bb1e-3f4b-49fb-b9ba-44407ca223b5"));
assert!(back.contains("test_str_uda"));
assert!(back.contains("test_str_uda_value"));
assert!(back.contains("test_float_uda"));
assert!(back.contains("-17.1234"));
assert!(back.contains("test_int_uda"));
assert!(back.contains("1234"));
}
#[test]
fn test_priority() {
let s = r#"{
"id":9,
"description":"Some long description for a task",
"entry":"20201021T065503Z",
"estimate":"30",
"modified":"20210213T233603Z",
"priority":"U",
"status":"pending",
"uuid":"6c4c9ee8-d6c4-4d64-a84d-bf9cb710684e",
"urgency":23
}"#;
println!("{}", s);
let task = serde_json::from_str(s);
println!("{:?}", task);
assert!(task.is_ok());
let task: Task = task.unwrap();
if let Some(priority) = task.priority() {
assert_eq!(*priority, "U".to_string());
} else {
panic!("Priority completely missing");
}
let back = serde_json::to_string_pretty(&task);
assert!(back.is_ok());
let back = back.unwrap();
println!("{}", back);
assert!(back.contains("description"));
assert!(back.contains("Some long description for a task"));
assert!(back.contains("entry"));
assert!(back.contains("20201021T065503Z"));
assert!(back.contains("priority"));
assert!(back.contains("status"));
assert!(back.contains("pending"));
assert!(back.contains("uuid"));
assert!(back.contains("6c4c9ee8-d6c4-4d64-a84d-bf9cb710684e"));
}
#[test]
fn test_builder_simple() {
use crate::task::TaskBuilder;
let t = TaskBuilder::default()
.description("test")
.entry(mkdate("20150619T165438Z"))
.build();
println!("{:?}", t);
assert!(t.is_ok());
let t = t.unwrap();
assert_eq!(t.status(), &TaskStatus::Pending);
assert_eq!(t.description(), "test");
assert_eq!(t.entry(), &mkdate("20150619T165438Z"));
}
#[test]
fn test_builder_extensive() {
use crate::task::TaskBuilder;
use crate::uda::{UDAValue, UDA};
let mut uda = UDA::new();
uda.insert(
"test_str_uda".into(),
UDAValue::Str("test_str_uda_value".into()),
);
uda.insert("test_int_uda".into(), UDAValue::U64(1234));
uda.insert("test_float_uda".into(), UDAValue::F64(-17.1234));
let t = TaskBuilder::default()
.description("test")
.entry(mkdate("20150619T165438Z"))
.id(192)
.modified(mkdate("20160423T125942Z"))
.project("project".to_owned())
.tags(vec!["search".to_owned(), "things".to_owned()])
.uda(uda)
.build();
println!("{:?}", t);
assert!(t.is_ok());
let t = t.unwrap();
assert!(t.id().is_some());
assert_eq!(t.id().unwrap(), 192);
assert_eq!(t.status(), &TaskStatus::Pending);
assert_eq!(t.description(), "test");
assert_eq!(t.entry(), &mkdate("20150619T165438Z"));
assert!(t.modified().is_some());
assert_eq!(t.modified().unwrap(), &mkdate("20160423T125942Z"));
assert!(t.project().is_some());
assert_eq!(t.project().unwrap(), "project");
assert!(t.tags().is_some());
assert!(t.urgency().is_none());
assert_eq!(
t.tags().unwrap(),
&vec!["search".to_owned(), "things".to_owned()]
);
}
#[test]
fn test_builder_defaults() {
use crate::task::TaskBuilder;
assert!(TaskBuilder::default()
.description("Nice Task")
.build()
.is_ok());
}
#[test]
fn test_builder_fail() {
use crate::task::TaskBuilder;
assert!(TaskBuilder::default().build().is_err());
}
}