Scope key prefixes to backlog in list and show views
Global prefix computation made keys longer than necessary when
viewing a single backlog. Scoping to backlog-local keys produces
shorter, more readable prefixes in context.
Assisted-by: Claude Opus 4.6 via pi
diff --git a/src/bin/ranger/commands/backlog.rs b/src/bin/ranger/commands/backlog.rs
index 7874ef4..5b54561 100644
--- a/src/bin/ranger/commands/backlog.rs
+++ b/src/bin/ranger/commands/backlog.rs
@@ -68,8 +68,8 @@ pub async fn run(pool: &SqlitePool, command: BacklogCommands, json: bool) -> Res
});
println!("{}", serde_json::to_string_pretty(&detail).unwrap());
} else {
- let all_keys = ops::task::all_keys(&mut conn).await?;
- let prefixes = key::unique_prefix_lengths(&all_keys);
+ let backlog_keys = ops::task::keys_for_backlog(&mut conn, backlog.id).await?;
+ let prefixes = key::unique_prefix_lengths(&backlog_keys);
print_backlog_detail(&backlog);
diff --git a/src/bin/ranger/commands/task.rs b/src/bin/ranger/commands/task.rs
index 417694f..892618a 100644
--- a/src/bin/ranger/commands/task.rs
+++ b/src/bin/ranger/commands/task.rs
@@ -178,15 +178,16 @@ pub async fn run(pool: &SqlitePool, command: TaskCommands, json: bool) -> Result
let mut conn = pool.acquire().await?;
let state = state.map(|s| s.parse::<State>()).transpose()?;
- let all_keys = ops::task::all_keys(&mut conn).await?;
- let prefixes = key::unique_prefix_lengths(&all_keys);
-
if let Some(backlog_name) = &backlog {
let bl = ops::backlog::get_by_name(&mut conn, backlog_name).await?;
+ let backlog_keys = ops::task::keys_for_backlog(&mut conn, bl.id).await?;
+ let prefixes = key::unique_prefix_lengths(&backlog_keys);
let tasks = ops::task::list(&mut conn, bl.id, state).await?;
output::print_list(&tasks, json, |t| print_task(t, &prefixes));
} else {
// List all tasks (no backlog filter)
+ let all_keys = ops::task::all_keys(&mut conn).await?;
+ let prefixes = key::unique_prefix_lengths(&all_keys);
let backlogs = ops::backlog::list(&mut conn).await?;
let mut all_tasks = Vec::new();
for bl in &backlogs {
diff --git a/src/ops/task.rs b/src/ops/task.rs
index 2e881df..d059ace 100644
--- a/src/ops/task.rs
+++ b/src/ops/task.rs
@@ -91,6 +91,17 @@ pub async fn all_keys(conn: &mut SqliteConnection) -> Result<Vec<String>, Ranger
Ok(rows.into_iter().map(|(k,)| k).collect())
}
+pub async fn keys_for_backlog(
+ conn: &mut SqliteConnection,
+ backlog_id: i64,
+) -> Result<Vec<String>, RangerError> {
+ let rows: Vec<(String,)> = sqlx::query_as("SELECT key FROM tasks WHERE backlog_id = ?")
+ .bind(backlog_id)
+ .fetch_all(&mut *conn)
+ .await?;
+ Ok(rows.into_iter().map(|(k,)| k).collect())
+}
+
pub async fn get_by_id(conn: &mut SqliteConnection, id: i64) -> Result<Task, RangerError> {
let query = format!("SELECT {TASK_COLUMNS} FROM tasks WHERE id = ?");
let task = sqlx::query_as::<_, Task>(&query)
@@ -1202,4 +1213,47 @@ mod tests {
let count = rebalance(&mut conn, bl.id).await.unwrap();
assert_eq!(count, 0);
}
+
+ #[tokio::test]
+ async fn keys_for_backlog_scoped_to_backlog() {
+ let pool = test_pool().await;
+ let mut conn = pool.acquire().await.unwrap();
+ let bl1 = backlog::create(&mut conn, "Alpha").await.unwrap();
+ let bl2 = backlog::create(&mut conn, "Beta").await.unwrap();
+
+ let t1 = create(
+ &mut conn,
+ CreateTask {
+ title: "Task in Alpha",
+ backlog_id: bl1.id,
+ state: None,
+ parent_id: None,
+ description: None,
+ },
+ )
+ .await
+ .unwrap();
+
+ let t2 = create(
+ &mut conn,
+ CreateTask {
+ title: "Task in Beta",
+ backlog_id: bl2.id,
+ state: None,
+ parent_id: None,
+ description: None,
+ },
+ )
+ .await
+ .unwrap();
+
+ let alpha_keys = keys_for_backlog(&mut conn, bl1.id).await.unwrap();
+ assert_eq!(alpha_keys, vec![t1.key.clone()]);
+
+ let beta_keys = keys_for_backlog(&mut conn, bl2.id).await.unwrap();
+ assert_eq!(beta_keys, vec![t2.key.clone()]);
+
+ let global_keys = all_keys(&mut conn).await.unwrap();
+ assert_eq!(global_keys.len(), 2);
+ }
}