create.rs

 1use anyhow::Result;
 2use std::path::Path;
 3
 4use crate::db;
 5
 6pub struct Opts<'a> {
 7    pub title: Option<&'a str>,
 8    pub priority: i32,
 9    pub effort: i32,
10    pub task_type: &'a str,
11    pub desc: Option<&'a str>,
12    pub parent: Option<&'a str>,
13    pub labels: Option<&'a str>,
14    pub json: bool,
15}
16
17pub fn run(root: &Path, opts: Opts) -> Result<()> {
18    let title = opts
19        .title
20        .ok_or_else(|| anyhow::anyhow!("title required"))?;
21    let desc = opts.desc.unwrap_or("");
22    let ts = db::now_utc();
23
24    let conn = db::open(root)?;
25
26    let id = match opts.parent {
27        Some(pid) => {
28            let count: i64 =
29                conn.query_row("SELECT COUNT(*) FROM tasks WHERE parent = ?1", [pid], |r| {
30                    r.get(0)
31                })?;
32            format!("{pid}.{}", count + 1)
33        }
34        None => db::gen_id(),
35    };
36
37    conn.execute(
38        "INSERT INTO tasks (id, title, description, type, priority, status, effort, parent, created, updated)
39         VALUES (?1, ?2, ?3, ?4, ?5, 'open', ?6, ?7, ?8, ?9)",
40        rusqlite::params![
41            id,
42            title,
43            desc,
44            opts.task_type,
45            opts.priority,
46            opts.effort,
47            opts.parent.unwrap_or(""),
48            ts,
49            ts
50        ],
51    )?;
52
53    if let Some(label_str) = opts.labels {
54        for lbl in label_str.split(',') {
55            let lbl = lbl.trim();
56            if !lbl.is_empty() {
57                conn.execute(
58                    "INSERT OR IGNORE INTO labels (task_id, label) VALUES (?1, ?2)",
59                    [&id, lbl],
60                )?;
61            }
62        }
63    }
64
65    if opts.json {
66        let task = db::Task {
67            id: id.clone(),
68            title: title.to_string(),
69            description: desc.to_string(),
70            task_type: opts.task_type.to_string(),
71            priority: opts.priority,
72            status: "open".to_string(),
73            effort: opts.effort,
74            parent: opts.parent.unwrap_or("").to_string(),
75            created: ts.clone(),
76            updated: ts,
77        };
78        println!("{}", serde_json::to_string(&task)?);
79    } else {
80        let c = crate::color::stdout_theme();
81        println!("{}created{} {id}: {title}", c.green, c.reset);
82    }
83
84    Ok(())
85}