Detailed changes
@@ -26,13 +26,13 @@ pub enum Command {
/// Task title
title: Option<String>,
- /// 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<String>,
- /// Filter by priority
+ /// Filter by priority (low, medium, high)
#[arg(short, long)]
- priority: Option<i32>,
+ priority: Option<String>,
+
+ /// Filter by effort (low, medium, high)
+ #[arg(short, long)]
+ effort: Option<String>,
/// Filter by label
#[arg(short, long)]
@@ -82,9 +86,13 @@ pub enum Command {
#[arg(short, long)]
status: Option<String>,
- /// Set priority
+ /// Set priority (low, medium, high)
+ #[arg(short, long)]
+ priority: Option<String>,
+
+ /// Set effort (low, medium, high)
#[arg(short, long)]
- priority: Option<i32>,
+ effort: Option<String>,
/// Set title
#[arg(short = 't', long)]
@@ -7,6 +7,7 @@ pub fn run(
root: &Path,
status: Option<&str>,
priority: Option<i32>,
+ effort: Option<i32>,
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,
);
@@ -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,
@@ -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
);
}
}
@@ -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);
@@ -6,6 +6,7 @@ use crate::db;
pub struct Opts<'a> {
pub status: Option<&'a str>,
pub priority: Option<i32>,
+ pub effort: Option<i32>,
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()));
@@ -49,6 +49,50 @@ pub struct TaskDetail {
pub blockers: Vec<String>,
}
+/// Parse a priority label to its integer value.
+///
+/// Accepts "low" (3), "medium" (2), or "high" (1).
+pub fn parse_priority(s: &str) -> anyhow::Result<i32> {
+ 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<i32> {
+ 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()
@@ -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()
@@ -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();
@@ -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();