diff --git a/src/cli.rs b/src/cli.rs index ee3b010f5ed75f0b7ea9299c453d8092a8d893ce..3101d3097851adef69f431d2834a10a26a0bfe67 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -26,13 +26,13 @@ pub enum Command { /// Task title title: Option, - /// Priority level (1=high, 2=medium, 3=low) - #[arg(short, long, default_value_t = 2)] - priority: i32, + /// Priority (low, medium, high) + #[arg(short, long, default_value = "medium")] + priority: String, - /// Effort level (1=low, 2=medium, 3=high) - #[arg(short, long, default_value_t = 2)] - effort: i32, + /// Effort (low, medium, high) + #[arg(short, long, default_value = "medium")] + effort: String, /// Task type #[arg(short = 't', long = "type", default_value = "task")] @@ -58,9 +58,13 @@ pub enum Command { #[arg(short, long)] status: Option, - /// Filter by priority + /// Filter by priority (low, medium, high) #[arg(short, long)] - priority: Option, + priority: Option, + + /// Filter by effort (low, medium, high) + #[arg(short, long)] + effort: Option, /// Filter by label #[arg(short, long)] @@ -82,9 +86,13 @@ pub enum Command { #[arg(short, long)] status: Option, - /// Set priority + /// Set priority (low, medium, high) + #[arg(short, long)] + priority: Option, + + /// Set effort (low, medium, high) #[arg(short, long)] - priority: Option, + effort: Option, /// Set title #[arg(short = 't', long)] diff --git a/src/cmd/list.rs b/src/cmd/list.rs index 6637d323a0c87b3e227e4465a6d71952e29d01a7..0eb1784aaca055706be249e3d440678747b14df5 100644 --- a/src/cmd/list.rs +++ b/src/cmd/list.rs @@ -7,6 +7,7 @@ pub fn run( root: &Path, status: Option<&str>, priority: Option, + effort: Option, label: Option<&str>, json: bool, ) -> Result<()> { @@ -29,6 +30,11 @@ pub fn run( params.push(Box::new(p)); idx += 1; } + if let Some(e) = effort { + sql.push_str(&format!(" AND effort = ?{idx}")); + params.push(Box::new(e)); + idx += 1; + } if let Some(l) = label { sql.push_str(&format!( " AND id IN (SELECT task_id FROM labels WHERE label = ?{idx})" @@ -70,7 +76,7 @@ pub fn run( format!("[{}]", t.status), c.reset, c.red, - format!("P{}", t.priority), + db::priority_label(t.priority), c.reset, t.title, ); diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 090b271a359f9f2d9cb4ed977a2aa2a5870e7b32..34560faa7f7642a48f850cd94db394fc0e14720d 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -43,8 +43,8 @@ pub fn dispatch(cli: &Cli) -> Result<()> { &root, create::Opts { title: title.as_deref(), - priority: *priority, - effort: *effort, + priority: db::parse_priority(priority)?, + effort: db::parse_effort(effort)?, task_type, desc: desc.as_deref(), parent: parent.as_deref(), @@ -56,13 +56,17 @@ pub fn dispatch(cli: &Cli) -> Result<()> { Command::List { status, priority, + effort, label, } => { let root = require_root()?; + let pri = priority.as_deref().map(db::parse_priority).transpose()?; + let eff = effort.as_deref().map(db::parse_effort).transpose()?; list::run( &root, status.as_deref(), - *priority, + pri, + eff, label.as_deref(), cli.json, ) @@ -75,16 +79,20 @@ pub fn dispatch(cli: &Cli) -> Result<()> { id, status, priority, + effort, title, desc, } => { let root = require_root()?; + let pri = priority.as_deref().map(db::parse_priority).transpose()?; + let eff = effort.as_deref().map(db::parse_effort).transpose()?; update::run( &root, id, update::Opts { status: status.as_deref(), - priority: *priority, + priority: pri, + effort: eff, title: title.as_deref(), desc: desc.as_deref(), json: cli.json, diff --git a/src/cmd/ready.rs b/src/cmd/ready.rs index b5e75b6cc0a1cb10d6f8d7cd0a32fd64973907c1..9fe4d6f784b4c3ba52aa3c6ce33a42349b155fcd 100644 --- a/src/cmd/ready.rs +++ b/src/cmd/ready.rs @@ -29,7 +29,8 @@ pub fn run(root: &Path, json: bool) -> Result<()> { serde_json::json!({ "id": t.id, "title": t.title, - "priority": t.priority, + "priority": db::priority_label(t.priority), + "effort": db::effort_label(t.effort), }) }) .collect(); @@ -38,8 +39,14 @@ pub fn run(root: &Path, json: bool) -> Result<()> { let c = crate::color::stdout_theme(); for t in &tasks { println!( - "{}{:<12}{} {}P{:<3}{} {}", - c.green, t.id, c.reset, c.red, t.priority, c.reset, t.title + "{}{:<12}{} {}{:<8}{} {}", + c.green, + t.id, + c.reset, + c.red, + db::priority_label(t.priority), + c.reset, + t.title ); } } diff --git a/src/cmd/show.rs b/src/cmd/show.rs index 4e689da657e9cca48e561c3222b2483c7497f4a5..25cc9f714208c6d7d10395ccc267d5e64038ac7f 100644 --- a/src/cmd/show.rs +++ b/src/cmd/show.rs @@ -24,8 +24,18 @@ pub fn run(root: &Path, id: &str, json: bool) -> Result<()> { println!("{} id{} = {}", c.bold, c.reset, t.id); 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!( + "{} priority{} = {}", + c.bold, + c.reset, + db::priority_label(t.priority) + ); + println!( + "{} effort{} = {}", + c.bold, + c.reset, + db::effort_label(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/cmd/update.rs b/src/cmd/update.rs index d2301748d8c7f1b606d8d65040b54db8467779d2..2fd3be6d92498a04a3373d3b6822de550a0e02a8 100644 --- a/src/cmd/update.rs +++ b/src/cmd/update.rs @@ -6,6 +6,7 @@ use crate::db; pub struct Opts<'a> { pub status: Option<&'a str>, pub priority: Option, + pub effort: Option, pub title: Option<&'a str>, pub desc: Option<&'a str>, pub json: bool, @@ -29,6 +30,11 @@ pub fn run(root: &Path, id: &str, opts: Opts) -> Result<()> { params.push(Box::new(p)); idx += 1; } + if let Some(e) = opts.effort { + sets.push(format!("effort = ?{idx}")); + params.push(Box::new(e)); + idx += 1; + } if let Some(t) = opts.title { sets.push(format!("title = ?{idx}")); params.push(Box::new(t.to_string())); diff --git a/src/db.rs b/src/db.rs index 8d4bec2c35d4535c675857deb3161a02b7628f8b..02778295386a02824d1966a138318e185a514d9c 100644 --- a/src/db.rs +++ b/src/db.rs @@ -49,6 +49,50 @@ pub struct TaskDetail { pub blockers: Vec, } +/// Parse a priority label to its integer value. +/// +/// Accepts "low" (3), "medium" (2), or "high" (1). +pub fn parse_priority(s: &str) -> anyhow::Result { + match s { + "high" => Ok(1), + "medium" => Ok(2), + "low" => Ok(3), + _ => bail!("invalid priority '{s}': expected low, medium, or high"), + } +} + +/// Convert a priority integer back to its label. +pub fn priority_label(val: i32) -> &'static str { + match val { + 1 => "high", + 2 => "medium", + 3 => "low", + _ => "unknown", + } +} + +/// Parse an effort label to its integer value. +/// +/// Accepts "low" (1), "medium" (2), or "high" (3). +pub fn parse_effort(s: &str) -> anyhow::Result { + match s { + "low" => Ok(1), + "medium" => Ok(2), + "high" => Ok(3), + _ => bail!("invalid effort '{s}': expected low, medium, or high"), + } +} + +/// Convert an effort integer back to its label. +pub fn effort_label(val: i32) -> &'static str { + match val { + 1 => "low", + 2 => "medium", + 3 => "high", + _ => "unknown", + } +} + /// Current UTC time in ISO 8601 format. pub fn now_utc() -> String { chrono::Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string() diff --git a/tests/cli_create.rs b/tests/cli_create.rs index 8f3fbe2763f0fb4a3b7b78be89cde2cf40fb5437..d526983dce85f325feb3c21e7c8c6511bd363551 100644 --- a/tests/cli_create.rs +++ b/tests/cli_create.rs @@ -41,7 +41,7 @@ fn create_json_returns_task_object() { fn create_with_priority_and_type() { let tmp = init_tmp(); - td().args(["--json", "create", "Urgent bug", "-p", "1", "-t", "bug"]) + td().args(["--json", "create", "Urgent bug", "-p", "high", "-t", "bug"]) .current_dir(&tmp) .assert() .success() diff --git a/tests/cli_list_show.rs b/tests/cli_list_show.rs index 0e4b0643bd7ddd0c1dd615979b1d1a956bf7fffd..bc1f1246892f6676eebc2bdcbb569aa83db87fc0 100644 --- a/tests/cli_list_show.rs +++ b/tests/cli_list_show.rs @@ -74,17 +74,17 @@ fn list_filter_by_status() { fn list_filter_by_priority() { let tmp = init_tmp(); - td().args(["create", "Low prio", "-p", "3"]) + td().args(["create", "Low prio", "-p", "low"]) .current_dir(&tmp) .assert() .success(); - td().args(["create", "High prio", "-p", "1"]) + td().args(["create", "High prio", "-p", "high"]) .current_dir(&tmp) .assert() .success(); let out = td() - .args(["--json", "list", "-p", "1"]) + .args(["--json", "list", "-p", "high"]) .current_dir(&tmp) .output() .unwrap(); diff --git a/tests/cli_update.rs b/tests/cli_update.rs index 531074c2e1d599f4a274be3ee86f4f1c4c042a53..3459131e727a08e91dab4e7ffcee37e099f6b4b2 100644 --- a/tests/cli_update.rs +++ b/tests/cli_update.rs @@ -53,7 +53,7 @@ fn update_changes_priority() { let tmp = init_tmp(); let id = create_task(&tmp, "Reprioritise"); - td().args(["update", &id, "-p", "1"]) + td().args(["update", &id, "-p", "high"]) .current_dir(&tmp) .assert() .success(); @@ -96,7 +96,7 @@ fn update_json_returns_task() { let id = create_task(&tmp, "JSON update"); let out = td() - .args(["--json", "update", &id, "-p", "1"]) + .args(["--json", "update", &id, "-p", "high"]) .current_dir(&tmp) .output() .unwrap();