1use anyhow::Result;
2use serde::Deserialize;
3use std::io::BufRead;
4use std::path::Path;
5
6use crate::db;
7
8#[derive(Deserialize)]
9struct ImportTask {
10 id: String,
11 title: String,
12 #[serde(default)]
13 description: String,
14 #[serde(rename = "type", default = "default_type")]
15 task_type: String,
16 #[serde(default = "default_priority")]
17 priority: i32,
18 #[serde(default = "default_status")]
19 status: String,
20 #[serde(default)]
21 parent: String,
22 created: String,
23 updated: String,
24 #[serde(default)]
25 labels: Vec<String>,
26 #[serde(default)]
27 blockers: Vec<String>,
28}
29
30fn default_type() -> String {
31 "task".into()
32}
33fn default_priority() -> i32 {
34 2
35}
36fn default_status() -> String {
37 "open".into()
38}
39
40pub fn run(root: &Path, file: &str) -> Result<()> {
41 let conn = db::open(root)?;
42
43 eprintln!("info: importing from {file}...");
44
45 let reader: Box<dyn BufRead> = if file == "-" {
46 Box::new(std::io::stdin().lock())
47 } else {
48 Box::new(std::io::BufReader::new(std::fs::File::open(file)?))
49 };
50
51 for line in reader.lines() {
52 let line = line?;
53 if line.trim().is_empty() {
54 continue;
55 }
56
57 let t: ImportTask = serde_json::from_str(&line)?;
58
59 conn.execute(
60 "INSERT OR REPLACE INTO tasks
61 (id, title, description, type, priority, status, parent, created, updated)
62 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
63 rusqlite::params![
64 t.id,
65 t.title,
66 t.description,
67 t.task_type,
68 t.priority,
69 t.status,
70 t.parent,
71 t.created,
72 t.updated,
73 ],
74 )?;
75
76 // Replace labels.
77 conn.execute("DELETE FROM labels WHERE task_id = ?1", [&t.id])?;
78 for lbl in &t.labels {
79 conn.execute(
80 "INSERT INTO labels (task_id, label) VALUES (?1, ?2)",
81 [&t.id, lbl],
82 )?;
83 }
84
85 // Replace blockers.
86 conn.execute("DELETE FROM blockers WHERE task_id = ?1", [&t.id])?;
87 for blk in &t.blockers {
88 conn.execute(
89 "INSERT INTO blockers (task_id, blocker_id) VALUES (?1, ?2)",
90 [&t.id, blk],
91 )?;
92 }
93 }
94
95 eprintln!("info: import complete");
96 Ok(())
97}