diff --git a/src/cli.rs b/src/cli.rs index 8c25b5cda32884530c08036ae89ffd826f25d297..ee3b010f5ed75f0b7ea9299c453d8092a8d893ce 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -30,6 +30,10 @@ pub enum Command { #[arg(short, long, default_value_t = 2)] priority: i32, + /// Effort level (1=low, 2=medium, 3=high) + #[arg(short, long, default_value_t = 2)] + effort: i32, + /// Task type #[arg(short = 't', long = "type", default_value = "task")] task_type: String, diff --git a/src/cmd/create.rs b/src/cmd/create.rs index cd9b782e188f523971e3533a30e76d0a91d659b8..a617ef1d79b07d41475c5c3919c5f9e847cc0b05 100644 --- a/src/cmd/create.rs +++ b/src/cmd/create.rs @@ -6,6 +6,7 @@ use crate::db; pub struct Opts<'a> { pub title: Option<&'a str>, pub priority: i32, + pub effort: i32, pub task_type: &'a str, pub desc: Option<&'a str>, pub parent: Option<&'a str>, @@ -34,14 +35,15 @@ pub fn run(root: &Path, opts: Opts) -> Result<()> { }; conn.execute( - "INSERT INTO tasks (id, title, description, type, priority, status, parent, created, updated) - VALUES (?1, ?2, ?3, ?4, ?5, 'open', ?6, ?7, ?8)", + "INSERT INTO tasks (id, title, description, type, priority, status, effort, parent, created, updated) + VALUES (?1, ?2, ?3, ?4, ?5, 'open', ?6, ?7, ?8, ?9)", rusqlite::params![ id, title, desc, opts.task_type, opts.priority, + opts.effort, opts.parent.unwrap_or(""), ts, ts @@ -68,6 +70,7 @@ pub fn run(root: &Path, opts: Opts) -> Result<()> { task_type: opts.task_type.to_string(), priority: opts.priority, status: "open".to_string(), + effort: opts.effort, parent: opts.parent.unwrap_or("").to_string(), created: ts.clone(), updated: ts, diff --git a/src/cmd/export.rs b/src/cmd/export.rs index 40014863a11010ba31a3819bce1ea33b4677dc19..f212cc70f3087999b930fd5d2cc88a04a6810aaf 100644 --- a/src/cmd/export.rs +++ b/src/cmd/export.rs @@ -7,7 +7,7 @@ pub fn run(root: &Path) -> Result<()> { let conn = db::open(root)?; let mut stmt = conn.prepare( - "SELECT id, title, description, type, priority, status, parent, created, updated + "SELECT id, title, description, type, priority, status, effort, parent, created, updated FROM tasks ORDER BY id", )?; @@ -26,6 +26,7 @@ pub fn run(root: &Path) -> Result<()> { task_type: t.task_type.clone(), priority: t.priority, status: t.status.clone(), + effort: t.effort, parent: t.parent.clone(), created: t.created.clone(), updated: t.updated.clone(), diff --git a/src/cmd/import.rs b/src/cmd/import.rs index 23c47b52b47a1b0865b6085ffc5b6f01893f898f..ad0fe3f2fd2e2f71164958ea7b26327e992e1943 100644 --- a/src/cmd/import.rs +++ b/src/cmd/import.rs @@ -17,6 +17,8 @@ struct ImportTask { priority: i32, #[serde(default = "default_status")] status: String, + #[serde(default = "default_effort")] + effort: i32, #[serde(default)] parent: String, created: String, @@ -36,6 +38,9 @@ fn default_priority() -> i32 { fn default_status() -> String { "open".into() } +fn default_effort() -> i32 { + 2 +} pub fn run(root: &Path, file: &str) -> Result<()> { let conn = db::open(root)?; @@ -58,8 +63,8 @@ pub fn run(root: &Path, file: &str) -> Result<()> { conn.execute( "INSERT OR REPLACE INTO tasks - (id, title, description, type, priority, status, parent, created, updated) - VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)", + (id, title, description, type, priority, status, effort, parent, created, updated) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)", rusqlite::params![ t.id, t.title, @@ -67,6 +72,7 @@ pub fn run(root: &Path, file: &str) -> Result<()> { t.task_type, t.priority, t.status, + t.effort, t.parent, t.created, t.updated, diff --git a/src/cmd/list.rs b/src/cmd/list.rs index 4def99d9a349557d25e93c6f89888694ae208dca..6637d323a0c87b3e227e4465a6d71952e29d01a7 100644 --- a/src/cmd/list.rs +++ b/src/cmd/list.rs @@ -13,7 +13,7 @@ pub fn run( let conn = db::open(root)?; let mut sql = String::from( - "SELECT id, title, description, type, priority, status, parent, created, updated + "SELECT id, title, description, type, priority, status, effort, parent, created, updated FROM tasks WHERE 1=1", ); let mut params: Vec> = Vec::new(); diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 52ed422b69e79e8d89b0d6e04a5a11c3df4ccc53..090b271a359f9f2d9cb4ed977a2aa2a5870e7b32 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -32,6 +32,7 @@ pub fn dispatch(cli: &Cli) -> Result<()> { Command::Create { title, priority, + effort, task_type, desc, parent, @@ -43,6 +44,7 @@ pub fn dispatch(cli: &Cli) -> Result<()> { create::Opts { title: title.as_deref(), priority: *priority, + effort: *effort, task_type, desc: desc.as_deref(), parent: parent.as_deref(), diff --git a/src/cmd/ready.rs b/src/cmd/ready.rs index b0cfd129260912847eb6b95003b2910e34362a8e..b5e75b6cc0a1cb10d6f8d7cd0a32fd64973907c1 100644 --- a/src/cmd/ready.rs +++ b/src/cmd/ready.rs @@ -7,7 +7,7 @@ pub fn run(root: &Path, json: bool) -> Result<()> { let conn = db::open(root)?; let mut stmt = conn.prepare( - "SELECT id, title, description, type, priority, status, parent, created, updated + "SELECT id, title, description, type, priority, status, effort, parent, created, updated FROM tasks WHERE status = 'open' AND id NOT IN ( diff --git a/src/cmd/search.rs b/src/cmd/search.rs index 91400b66fd6b7138575f336be106db93bf84dc7d..12cebb75af4f84e77f6bccf09bd15a3e272edbb1 100644 --- a/src/cmd/search.rs +++ b/src/cmd/search.rs @@ -8,7 +8,7 @@ pub fn run(root: &Path, query: &str, json: bool) -> Result<()> { let pattern = format!("%{query}%"); let mut stmt = conn.prepare( - "SELECT id, title, description, type, priority, status, parent, created, updated + "SELECT id, title, description, type, priority, status, effort, parent, created, updated FROM tasks WHERE title LIKE ?1 OR description LIKE ?1", )?; diff --git a/src/cmd/show.rs b/src/cmd/show.rs index 385016d9f04800238dd0dd1219878ae5519d9fb8..4e689da657e9cca48e561c3222b2483c7497f4a5 100644 --- a/src/cmd/show.rs +++ b/src/cmd/show.rs @@ -25,6 +25,7 @@ pub fn run(root: &Path, id: &str, json: bool) -> Result<()> { println!("{} title{} = {}", c.bold, c.reset, t.title); println!("{} status{} = {}", c.bold, c.reset, t.status); println!("{} priority{} = {}", c.bold, c.reset, t.priority); + println!("{} effort{} = {}", c.bold, c.reset, t.effort); println!("{} type{} = {}", c.bold, c.reset, t.task_type); if !t.description.is_empty() { println!("{} description{} = {}", c.bold, c.reset, t.description); diff --git a/src/db.rs b/src/db.rs index ccc95040c8a48551a4922b16e65ef4772867a2dc..8d4bec2c35d4535c675857deb3161a02b7628f8b 100644 --- a/src/db.rs +++ b/src/db.rs @@ -19,6 +19,7 @@ pub struct Task { pub task_type: String, pub priority: i32, pub status: String, + pub effort: i32, pub parent: String, pub created: String, pub updated: String, @@ -62,6 +63,7 @@ pub fn row_to_task(row: &rusqlite::Row) -> rusqlite::Result { task_type: row.get("type")?, priority: row.get("priority")?, status: row.get("status")?, + effort: row.get("effort")?, parent: row.get("parent")?, created: row.get("created")?, updated: row.get("updated")?, @@ -89,7 +91,7 @@ pub fn load_blockers(conn: &Connection, task_id: &str) -> Result> { /// Load a full task with labels and blockers. pub fn load_task_detail(conn: &Connection, id: &str) -> Result { let task = conn.query_row( - "SELECT id, title, description, type, priority, status, parent, created, updated + "SELECT id, title, description, type, priority, status, effort, parent, created, updated FROM tasks WHERE id = ?1", [id], row_to_task, diff --git a/src/migrate.rs b/src/migrate.rs index e5dad9633bf1f871cd2aaf167ed12d2ab5c6970f..069541f489a7543d1542b06eb1f844c0781de3b2 100644 --- a/src/migrate.rs +++ b/src/migrate.rs @@ -26,6 +26,13 @@ static MIGRATIONS: &[Migration] = &[ post_hook_up: None, post_hook_down: None, }, + // 1 → 2: add effort column (integer-backed, default medium) + Migration { + up_sql: include_str!("migrations/0002_add_effort.up.sql"), + down_sql: include_str!("migrations/0002_add_effort.down.sql"), + post_hook_up: None, + post_hook_down: None, + }, ]; /// Read the current schema version from the database. diff --git a/src/migrations/0002_add_effort.down.sql b/src/migrations/0002_add_effort.down.sql new file mode 100644 index 0000000000000000000000000000000000000000..559eafc10acba632f3ae8fdc571b4ff7d5cb94dd --- /dev/null +++ b/src/migrations/0002_add_effort.down.sql @@ -0,0 +1 @@ +ALTER TABLE tasks DROP COLUMN effort; diff --git a/src/migrations/0002_add_effort.up.sql b/src/migrations/0002_add_effort.up.sql new file mode 100644 index 0000000000000000000000000000000000000000..f88cc4c429153c06a1343579562d3919bf7b2812 --- /dev/null +++ b/src/migrations/0002_add_effort.up.sql @@ -0,0 +1 @@ +ALTER TABLE tasks ADD COLUMN effort INTEGER NOT NULL DEFAULT 2;