horae 0.2.5

Capture a task in under 10 seconds. GTD terminal manager (ratatui TUI + CLI) with ~2 ms startup, reactive completions, recurring habits, pomodoro, and phone sync. Rust + SQLite, fully offline.
use std::collections::BTreeMap;

use rusqlite::Connection;

use anyhow::Result;
use horae_core::model::tag::Tag;
use horae_core::repo::{tags, tasks};

pub fn add(conn: &Connection, id: &str, name: &str) -> Result<()> {
    let id = tasks::resolve_id(conn, id)?;
    let _ = tasks::get(conn, &id)?;
    tags::add_tag_to_task(conn, &id, name)?;
    println!("tagged {} with {}", &id[..id.len().min(8)], name);
    Ok(())
}

pub fn remove(conn: &Connection, id: &str, name: &str) -> Result<()> {
    let id = tasks::resolve_id(conn, id)?;
    let _ = tasks::get(conn, &id)?;
    tags::remove_tag_from_task(conn, &id, name)?;
    println!("untagged {} from {}", &id[..id.len().min(8)], name);
    Ok(())
}

pub fn list(conn: &Connection) -> Result<()> {
    let all = tags::list_tags(conn)?;
    let mut by_cat: BTreeMap<String, Vec<Tag>> = BTreeMap::new();
    for t in all {
        by_cat.entry(t.category.clone()).or_default().push(t);
    }
    for (cat, items) in by_cat {
        println!("[{}]", cat);
        for t in items {
            let kind = if t.is_system { "sys" } else { "usr" };
            println!("  {}  ({})", t.name, kind);
        }
    }
    Ok(())
}