import.rs

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