task_hookrs/
import.rs

1//
2// This Source Code Form is subject to the terms of the Mozilla Public
3// License, v. 2.0. If a copy of the MPL was not distributed with this
4// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5//
6
7//! Module containing the `import()` function
8
9use std::io::BufRead;
10use std::io::Read;
11
12use serde_json;
13
14use crate::error::Error;
15use crate::task::{Task, TaskWarriorVersion};
16
17/// Import taskwarrior-exported JSON. This expects an JSON Array of objects, as exported by
18/// taskwarrior.
19pub fn import<T: TaskWarriorVersion, R: Read>(r: R) -> Result<Vec<Task<T>>, Error> {
20    serde_json::from_reader(r).map_err(Error::from)
21}
22
23/// Import a single JSON-formatted Task
24pub fn import_task<T: TaskWarriorVersion>(s: &str) -> Result<Task<T>, Error> {
25    serde_json::from_str(s).map_err(Error::from)
26}
27
28/// Reads line by line and tries to parse a task-object per line.
29pub fn import_tasks<T: TaskWarriorVersion, BR: BufRead>(r: BR) -> Vec<Result<Task<T>, Error>> {
30    let mut vt = Vec::new();
31    for line in r.lines() {
32        if let Err(err) = line {
33            vt.push(Err(Error::from(err)));
34            continue;
35        }
36        // Unwrap is safe because of continue above
37        if line.as_ref().unwrap().is_empty() {
38            // Empty strings are not usable, and shall be silently ignored
39            continue;
40        }
41        vt.push(import_task(line.unwrap().as_str()));
42    }
43    vt
44}
45
46#[cfg(test)]
47mod test {
48    use crate::import::{import, import_task, import_tasks};
49    use crate::task::{Task, TW25, TW26};
50
51    #[test]
52    fn test_one_tw25() {
53        let s = r#"
54[
55    {
56        "id": 1,
57        "description": "some description",
58        "entry": "20150619T165438Z",
59        "modified": "20160327T164007Z",
60        "project": "someproject",
61        "status": "waiting",
62        "tags": ["some", "tags", "are", "here"],
63        "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
64        "depends": "8ca953d5-18b5-4eb9-bd56-18f2e5b752f0",
65        "wait": "20160508T164007Z",
66        "urgency": 0.583562
67    }
68]
69"#;
70
71        let imported = import::<TW25, _>(s.as_bytes());
72        assert!(imported.is_ok());
73        let imported = imported.unwrap();
74        assert!(imported.len() == 1);
75    }
76
77    #[test]
78    fn test_one_tw26() {
79        let s = r#"
80[
81    {
82        "id": 1,
83        "description": "some description",
84        "entry": "20150619T165438Z",
85        "modified": "20160327T164007Z",
86        "project": "someproject",
87        "status": "waiting",
88        "tags": ["some", "tags", "are", "here"],
89        "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
90        "depends": ["8ca953d5-18b5-4eb9-bd56-18f2e5b752f0"],
91        "wait": "20160508T164007Z",
92        "urgency": 0.583562
93    }
94]
95"#;
96
97        let imported = import::<TW26, _>(s.as_bytes());
98        assert!(imported.is_ok());
99        let imported = imported.unwrap();
100        assert!(imported.len() == 1);
101    }
102
103    #[test]
104    fn test_two_tw25() {
105        let s = r#"
106[
107    {
108        "id"          : 1,
109        "description" : "test",
110        "entry"       : "20150619T165438Z",
111        "modified"    : "20160327T164007Z",
112        "project"     : "self.software",
113        "status"      : "waiting",
114        "tags"        : ["check", "this", "crate", "out"],
115        "uuid"        : "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
116        "wait"        : "20160508T164007Z",
117        "urgency"     : 0.583562
118    },
119    {
120        "id"          : 2,
121        "description" : "another test",
122        "entry"       : "20150623T181011Z",
123        "modified"    : "20160327T163718Z",
124        "priority"    : "L",
125        "project"     : "studying",
126        "status"      : "waiting",
127        "tags"        : ["I", "have", "awesome", "kittens"],
128        "uuid"        : "54d49ffc-a06b-4dd8-b7d1-db5f50594312",
129        "wait"        : "20160508T163718Z",
130        "annotations" : [
131            {
132                "entry"       : "20150623T181018Z",
133                "description" : "fooooooobar"
134            }
135        ],
136        "urgency"     : 3.16164
137    },
138    {
139        "id"          : 3,
140        "description" : "I love kittens, really!",
141        "entry"       : "20150919T222323Z",
142        "modified"    : "20160327T163718Z",
143        "project"     : "getkittens",
144        "status"      : "waiting",
145        "tags"        : ["kittens", "are", "so", "damn", "awesome"],
146        "uuid"        : "08ee8dce-cb97-4c8c-9940-c9a440e90119",
147        "wait"        : "20160508T163718Z",
148        "urgency"     : 1.07397
149    }
150]
151
152"#;
153
154        assert!(import::<TW25, _>(s.as_bytes()).unwrap().len() == 3);
155    }
156
157    #[test]
158    fn test_one_single_tw25() {
159        use crate::date::Date;
160        use crate::date::TASKWARRIOR_DATETIME_TEMPLATE;
161        use crate::status::TaskStatus;
162        use chrono::NaiveDateTime;
163        use uuid::Uuid;
164        fn mkdate(s: &str) -> Date {
165            let n = NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE);
166            Date::from(n.unwrap())
167        }
168        let s = r#"
169{
170    "id": 1,
171    "description": "some description",
172    "entry": "20150619T165438Z",
173    "modified": "20160327T164007Z",
174    "project": "someproject",
175    "status": "waiting",
176    "tags": ["some", "tags", "are", "here"],
177    "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
178    "wait": "20160508T164007Z",
179    "urgency": 0.583562
180}
181"#;
182        let imported = import_task(s);
183        assert!(imported.is_ok());
184
185        // Check for every information
186        let task: Task<TW25> = imported.unwrap();
187        assert_eq!(*task.status(), TaskStatus::Waiting);
188        assert_eq!(task.description(), "some description");
189        assert_eq!(*task.entry(), mkdate("20150619T165438Z"));
190        assert_eq!(
191            *task.uuid(),
192            Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap()
193        );
194        assert_eq!(task.modified(), Some(&mkdate("20160327T164007Z")));
195        assert_eq!(task.project(), Some(&String::from("someproject")));
196        if let Some(tags) = task.tags() {
197            for tag in tags {
198                let any_tag = ["some", "tags", "are", "here"].iter().any(|t| tag == *t);
199                assert!(any_tag, "Tag {} missing", tag);
200            }
201        } else {
202            panic!("Tags completely missing");
203        }
204
205        assert_eq!(task.wait(), Some(&mkdate("20160508T164007Z")));
206    }
207
208    #[test]
209    fn test_two_single_tw25() {
210        use crate::status::TaskStatus;
211        use std::io::BufReader;
212        let s = r#"
213{"id":1,"description":"some description","entry":"20150619T165438Z","modified":"20160327T164007Z","project":"someproject","status":"waiting","tags":["some","tags","are","here"],"uuid":"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","wait":"20160508T164007Z","urgency":0.583562}
214{"id":1,"description":"some description","entry":"20150619T165438Z","modified":"20160327T164007Z","project":"someproject","status":"waiting","tags":["some","tags","are","here"],"uuid":"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","wait":"20160508T164007Z","urgency":0.583562}"#;
215        let imported = import_tasks(BufReader::new(s.as_bytes()));
216        assert_eq!(imported.len(), 2);
217        assert!(imported[0].is_ok());
218        assert!(imported[1].is_ok());
219        let import0: &Task<TW25> = imported[0].as_ref().unwrap();
220        let import1 = imported[1].as_ref().unwrap();
221        assert_eq!(*import0.status(), TaskStatus::Waiting);
222        assert_eq!(*import1.status(), TaskStatus::Waiting);
223    }
224}