From 7a6160b3cc4766071e606dc0cc53d16dbf3b08c7 Mon Sep 17 00:00:00 2001 From: Cadu Date: Sun, 8 Mar 2026 18:48:39 -0300 Subject: [PATCH 1/5] refactor: simplify priority output and accept textual priority names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove raw numeric `priority` field from IssueSummary, SearchSummary, and IssueDetail; rename `priorityLabel` → `priority` in JSON output - Accept textual names (none, urgent, high, medium, low) in -p/--priority alongside numeric 0-4, case-insensitive - Invalid priority names produce a clear error listing valid options - Update usage, --help, and README to reflect new accepted values Closes #116 --- crates/lineark/README.md | 6 +- crates/lineark/src/commands/issues.rs | 82 ++++++++++--- crates/lineark/src/commands/usage.rs | 4 +- crates/lineark/tests/offline.rs | 26 ++++ crates/lineark/tests/online.rs | 170 ++++++++++++++++++++++++-- 5 files changed, 260 insertions(+), 28 deletions(-) diff --git a/crates/lineark/README.md b/crates/lineark/README.md index ea39b79..2c18d77 100644 --- a/crates/lineark/README.md +++ b/crates/lineark/README.md @@ -96,14 +96,14 @@ lineark issues search [-l N] Full-text search [--team KEY] [--assignee NAME-OR-ID|me] Filter by team, assignee, status [--status NAME,...] [--show-done] lineark issues create --team KEY Create an issue - [-p 0-4] [-e N] [--assignee NAME-OR-ID|me] Priority, estimate, assignee + [-p PRIORITY] [-e N] [--assignee NAME-OR-ID|me] Priority (0-4 or name), estimate [--labels NAME,...] [-s NAME] ... Labels, status — see --help lineark issues update <IDENTIFIER> Update an issue - [-s NAME] [-p 0-4] [-e N] Status, priority, estimate + [-s NAME] [-p PRIORITY] [-e N] Status, priority, estimate [--assignee NAME-OR-ID|me] Assignee [--clear-parent] [--project NAME-OR-ID] ... See --help for all options lineark issues batch-update ID [ID ...] Batch update multiple issues - [-s NAME] [-p 0-4] [--assignee NAME-OR-ID|me] Status, priority, assignee + [-s NAME] [-p PRIORITY] [--assignee ...] Status, priority, assignee lineark issues archive <IDENTIFIER> Archive an issue lineark issues unarchive <IDENTIFIER> Unarchive an issue lineark issues delete <IDENTIFIER> Delete (trash) an issue diff --git a/crates/lineark/src/commands/issues.rs b/crates/lineark/src/commands/issues.rs index c1610ca..01b85c1 100644 --- a/crates/lineark/src/commands/issues.rs +++ b/crates/lineark/src/commands/issues.rs @@ -16,6 +16,29 @@ use super::helpers::{ }; use crate::output::{self, Format}; +/// Parse a priority value from either a number (0-4) or a name (none, urgent, high, medium/normal, low). +fn parse_priority(s: &str) -> Result<i64, String> { + // Try numeric first. + if let Ok(n) = s.parse::<i64>() { + if (0..=4).contains(&n) { + return Ok(n); + } + return Err(format!( + "invalid priority '{n}': valid values are 0-4 or none, urgent, high, medium, low" + )); + } + match s.to_ascii_lowercase().as_str() { + "none" => Ok(0), + "urgent" => Ok(1), + "high" => Ok(2), + "medium" | "normal" => Ok(3), + "low" => Ok(4), + _ => Err(format!( + "invalid priority '{s}': valid values are 0-4 or none, urgent, high, medium, low" + )), + } +} + /// Manage issues. #[derive(Debug, Args)] pub struct IssuesCmd { @@ -72,8 +95,8 @@ pub enum IssuesAction { /// /// Examples: /// lineark issues create "Fix the bug" --team ENG - /// lineark issues create "Add feature" --team ENG --priority 2 --description "Details here" - /// lineark issues create "Urgent fix" --team ENG --priority 1 --labels Bug,Frontend + /// lineark issues create "Add feature" --team ENG --priority high --description "Details here" + /// lineark issues create "Urgent fix" --team ENG -p urgent --labels Bug,Frontend /// lineark issues create "My task" --team ENG --assignee me Create { /// Issue title. @@ -87,8 +110,8 @@ pub enum IssuesAction { /// Comma-separated label names. #[arg(long, value_delimiter = ',')] labels: Option<Vec<String>>, - /// Priority: 0=none, 1=urgent, 2=high, 3=medium, 4=low. - #[arg(short = 'p', long, value_parser = clap::value_parser!(i64).range(0..=4))] + /// Priority: 0-4 or none, urgent, high, medium, low. + #[arg(short = 'p', long, value_parser = parse_priority)] priority: Option<i64>, /// Estimate points (valid values depend on the team's estimation scale). #[arg(short = 'e', long)] @@ -145,7 +168,7 @@ pub enum IssuesAction { /// Batch update multiple issues at once. Returns the updated issues. /// /// Examples: - /// lineark issues batch-update ENG-1 ENG-2 --priority 2 + /// lineark issues batch-update ENG-1 ENG-2 --priority high /// lineark issues batch-update ENG-1 ENG-2 ENG-3 --status "In Progress" /// lineark issues batch-update ENG-1 ENG-2 --assignee me --labels Bug --label-by adding BatchUpdate { @@ -155,8 +178,8 @@ pub enum IssuesAction { /// New status name (resolved against the first issue's team workflow states). #[arg(short = 's', long)] status: Option<String>, - /// Priority: 0=none, 1=urgent, 2=high, 3=medium, 4=low. - #[arg(short = 'p', long, value_parser = clap::value_parser!(i64).range(0..=4))] + /// Priority: 0-4 or none, urgent, high, medium, low. + #[arg(short = 'p', long, value_parser = parse_priority)] priority: Option<i64>, /// Comma-separated label names. Behavior depends on --label-by. #[arg(long, value_delimiter = ',')] @@ -181,7 +204,7 @@ pub enum IssuesAction { /// /// Examples: /// lineark issues update ENG-123 --status "In Progress" - /// lineark issues update ENG-123 --priority 1 --assignee "John Doe" + /// lineark issues update ENG-123 --priority urgent --assignee "John Doe" /// lineark issues update ENG-123 --labels Bug,Frontend --label-by adding Update { /// Issue identifier (e.g., ENG-123) or UUID. @@ -189,8 +212,8 @@ pub enum IssuesAction { /// New status name (resolved against the issue's team workflow states). #[arg(short = 's', long)] status: Option<String>, - /// Priority: 0=none, 1=urgent, 2=high, 3=medium, 4=low. - #[arg(short = 'p', long, value_parser = clap::value_parser!(i64).range(0..=4))] + /// Priority: 0-4 or none, urgent, high, medium, low. + #[arg(short = 'p', long, value_parser = parse_priority)] priority: Option<i64>, /// Estimate points (valid values depend on the team's estimation scale). #[arg(short = 'e', long)] @@ -245,7 +268,7 @@ pub enum LabelMode { struct IssueRow { identifier: String, title: String, - #[serde(rename = "priorityLabel")] + #[serde(rename = "priority")] #[tabled(rename = "priority")] priority_label: String, #[tabled(rename = "status")] @@ -337,7 +360,7 @@ pub struct IssueSummary { pub id: Option<String>, pub identifier: Option<String>, pub title: Option<String>, - pub priority: Option<f64>, + #[serde(rename(serialize = "priority"))] pub priority_label: Option<String>, pub estimate: Option<f64>, pub url: Option<String>, @@ -359,7 +382,7 @@ pub struct SearchSummary { pub id: Option<String>, pub identifier: Option<String>, pub title: Option<String>, - pub priority: Option<f64>, + #[serde(rename(serialize = "priority"))] pub priority_label: Option<String>, pub estimate: Option<f64>, pub url: Option<String>, @@ -385,7 +408,7 @@ pub struct IssueDetail { pub identifier: Option<String>, pub title: Option<String>, pub description: Option<String>, - pub priority: Option<f64>, + #[serde(rename(serialize = "priority"))] pub priority_label: Option<String>, pub estimate: Option<f64>, pub url: Option<String>, @@ -1129,6 +1152,37 @@ async fn resolve_state_id( mod tests { use super::*; + #[test] + fn parse_priority_numeric_values() { + assert_eq!(parse_priority("0").unwrap(), 0); + assert_eq!(parse_priority("1").unwrap(), 1); + assert_eq!(parse_priority("4").unwrap(), 4); + } + + #[test] + fn parse_priority_textual_values() { + assert_eq!(parse_priority("none").unwrap(), 0); + assert_eq!(parse_priority("urgent").unwrap(), 1); + assert_eq!(parse_priority("high").unwrap(), 2); + assert_eq!(parse_priority("medium").unwrap(), 3); + assert_eq!(parse_priority("normal").unwrap(), 3); + assert_eq!(parse_priority("low").unwrap(), 4); + } + + #[test] + fn parse_priority_case_insensitive() { + assert_eq!(parse_priority("URGENT").unwrap(), 1); + assert_eq!(parse_priority("High").unwrap(), 2); + assert_eq!(parse_priority("LOW").unwrap(), 4); + } + + #[test] + fn parse_priority_rejects_invalid() { + assert!(parse_priority("bogus").is_err()); + assert!(parse_priority("5").is_err()); + assert!(parse_priority("-1").is_err()); + } + #[test] fn format_estimate_none_returns_empty() { assert_eq!(format_estimate(None), ""); diff --git a/crates/lineark/src/commands/usage.rs b/crates/lineark/src/commands/usage.rs index a013c3a..865d50b 100644 --- a/crates/lineark/src/commands/usage.rs +++ b/crates/lineark/src/commands/usage.rs @@ -87,12 +87,12 @@ COMMANDS: [--team KEY] [--assignee NAME-OR-ID|me] Filter by team, assignee, status [--status NAME,...] [--show-done] Comma-separated status names lineark issues create <TITLE> --team KEY Create an issue - [-p 0-4] [-e N] [--assignee NAME-OR-ID|me] 0=none 1=urgent 2=high 3=medium 4=low + [-p PRIORITY] [-e N] [--assignee NAME-OR-ID|me] 0-4 or none/urgent/high/medium/low [--labels NAME,...] [-d TEXT] [-s NAME] Label names, status name [--parent ID] [--project NAME-OR-ID] Parent issue, project, cycle [--cycle NAME-OR-ID] lineark issues update <IDENTIFIER> Update an issue - [-s NAME] [-p 0-4] [-e N] Status, priority, estimate + [-s NAME] [-p PRIORITY] [-e N] Status, priority, estimate [--assignee NAME-OR-ID|me] Assignee [--labels NAME,...] [--label-by adding|replacing|removing] [--clear-labels] [-t TEXT] [-d TEXT] Title, description diff --git a/crates/lineark/tests/offline.rs b/crates/lineark/tests/offline.rs index bb987a9..ef3f982 100644 --- a/crates/lineark/tests/offline.rs +++ b/crates/lineark/tests/offline.rs @@ -123,6 +123,32 @@ fn issues_create_help_shows_flags() { .stdout(predicate::str::contains("--labels")); } +#[test] +fn issues_create_rejects_invalid_priority_name() { + lineark() + .args([ + "issues", + "create", + "test", + "--team", + "FAKE", + "--priority", + "bogus", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid priority")); +} + +#[test] +fn issues_create_accepts_textual_priority_in_help() { + lineark() + .args(["issues", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("none, urgent, high, medium, low")); +} + #[test] fn issues_update_help_shows_flags() { lineark() diff --git a/crates/lineark/tests/online.rs b/crates/lineark/tests/online.rs index eebed47..52f4000 100644 --- a/crates/lineark/tests/online.rs +++ b/crates/lineark/tests/online.rs @@ -591,21 +591,22 @@ mod online { "assignee should be a flat string, got: {}", issue["assignee"] ); - // id and priority (numeric) must be absent + // id must be absent assert!( issue.get("id").is_none(), "id (UUID) should not be in list output" ); + // priority should be a text label, not numeric; priorityLabel should be gone assert!( - issue.get("priority").is_none(), - "priority (numeric) should not be in list output" + issue.get("priority").is_some_and(|v| v.is_string()), + "priority should be a text label in list output" ); - // url and priorityLabel must be present - assert!(issue.get("url").is_some(), "url should be in list output"); assert!( - issue.get("priorityLabel").is_some(), - "priorityLabel should be in list output" + issue.get("priorityLabel").is_none(), + "priorityLabel should not be in list output (renamed to priority)" ); + // url must be present + assert!(issue.get("url").is_some(), "url should be in list output"); } } @@ -685,8 +686,12 @@ mod online { "id (UUID) should not be in search output" ); assert!( - issue.get("priority").is_none(), - "priority (numeric) should not be in search output" + issue.get("priority").is_some_and(|v| v.is_string()), + "priority should be a text label in search output" + ); + assert!( + issue.get("priorityLabel").is_none(), + "priorityLabel should not be in search output (renamed to priority)" ); } } @@ -774,6 +779,153 @@ mod online { delete_issue(issue_id); } + // ── Issues create/update with textual priority names ─────────────────────── + + #[test_with::runtime_ignore_if(no_online_test_token)] + fn issues_create_with_textual_priority_and_update() { + let token = test_token(); + let unique_name = format!( + "[test] CLI textual priority {}", + &uuid::Uuid::new_v4().to_string()[..8] + ); + + let team = create_test_team(); + let team_key = team.key.clone(); + + // Create with --priority urgent (textual). + let output = run_lineark_with_retry(&[ + "--api-token", + &token, + "--format", + "json", + "issues", + "create", + &unique_name, + "--team", + &team_key, + "--priority", + "urgent", + ]); + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + output.status.success(), + "create with --priority urgent should succeed.\nstdout: {stdout}\nstderr: {stderr}" + ); + let created: serde_json::Value = serde_json::from_str(&stdout).unwrap(); + let issue_id = created["id"] + .as_str() + .expect("created issue should have id"); + let _issue_guard = IssueGuard { + token: token.clone(), + id: issue_id.to_string(), + }; + + // Read back and verify "priority": "Urgent". + let read_output = retry_with_backoff(5, || { + let out = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "issues", + "read", + issue_id, + ]) + .output() + .expect("failed to execute lineark"); + if !out.status.success() { + return Err(format!( + "issues read failed: {}", + String::from_utf8_lossy(&out.stderr) + )); + } + let json: serde_json::Value = + serde_json::from_slice(&out.stdout).map_err(|e| e.to_string())?; + let prio = json.get("priority").and_then(|v| v.as_str()).unwrap_or(""); + if prio == "Urgent" { + Ok(out) + } else { + Err(format!("priority is '{prio}', expected 'Urgent'")) + } + }) + .expect("reading issue with Urgent priority should succeed"); + let read_json: serde_json::Value = serde_json::from_slice(&read_output.stdout).unwrap(); + assert_eq!( + read_json.get("priority").and_then(|v| v.as_str()), + Some("Urgent"), + "read should show priority as 'Urgent'" + ); + // priorityLabel should not be in output. + assert!( + read_json.get("priorityLabel").is_none(), + "priorityLabel should not appear in read output" + ); + + // Update with --priority low (textual). + let output = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "issues", + "update", + issue_id, + "--priority", + "low", + ]) + .output() + .expect("failed to execute lineark"); + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + output.status.success(), + "update with --priority low should succeed.\nstdout: {stdout}\nstderr: {stderr}" + ); + + // Read back and verify "priority": "Low". + let read_output = retry_with_backoff(5, || { + let out = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "issues", + "read", + issue_id, + ]) + .output() + .expect("failed to execute lineark"); + if !out.status.success() { + return Err(format!( + "issues read failed: {}", + String::from_utf8_lossy(&out.stderr) + )); + } + let json: serde_json::Value = + serde_json::from_slice(&out.stdout).map_err(|e| e.to_string())?; + let prio = json.get("priority").and_then(|v| v.as_str()).unwrap_or(""); + if prio == "Low" { + Ok(out) + } else { + Err(format!("priority is '{prio}', expected 'Low'")) + } + }) + .expect("reading issue with Low priority should succeed"); + let read_json: serde_json::Value = serde_json::from_slice(&read_output.stdout).unwrap(); + assert_eq!( + read_json.get("priority").and_then(|v| v.as_str()), + Some("Low"), + "read should show priority as 'Low' after update" + ); + + // Clean up. + delete_issue(issue_id); + } + // ── Issues archive / unarchive ───────────────────────────────────────────── #[test_with::runtime_ignore_if(no_online_test_token)] From 113c415b88a24c0cfc93edc3e570c69f9e225c13 Mon Sep 17 00:00:00 2001 From: Cadu <cadu.coelho@gmail.com> Date: Sun, 8 Mar 2026 19:18:41 -0300 Subject: [PATCH 2/5] fix: consistent textual priority across all commands - Make parse_priority pub(crate) and use it in projects create - Add .trim() to parse_priority for whitespace robustness - Add unit test for whitespace handling - Fix README column alignment for projects create --- crates/lineark/README.md | 2 +- crates/lineark/src/commands/issues.rs | 10 +++++++++- crates/lineark/src/commands/projects.rs | 5 +++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/lineark/README.md b/crates/lineark/README.md index 2c18d77..997a65b 100644 --- a/crates/lineark/README.md +++ b/crates/lineark/README.md @@ -72,7 +72,7 @@ lineark projects create <NAME> --team KEY Create a project [--description TEXT] [--lead NAME-OR-ID|me] Description, lead, dates [--members NAME,...|me] Project members (comma-separated) [--start-date DATE] [--target-date DATE] Priority, content, icon, color - [-p 0-4] [--content TEXT] ... See --help for all options + [-p PRIORITY] [--content TEXT] ... See --help for all options lineark labels list [--team KEY] List labels (group, team, parent) lineark labels create <NAME> Create a label [--team KEY] [--color HEX] Team, color diff --git a/crates/lineark/src/commands/issues.rs b/crates/lineark/src/commands/issues.rs index 01b85c1..4ad2952 100644 --- a/crates/lineark/src/commands/issues.rs +++ b/crates/lineark/src/commands/issues.rs @@ -17,7 +17,8 @@ use super::helpers::{ use crate::output::{self, Format}; /// Parse a priority value from either a number (0-4) or a name (none, urgent, high, medium/normal, low). -fn parse_priority(s: &str) -> Result<i64, String> { +pub(crate) fn parse_priority(s: &str) -> Result<i64, String> { + let s = s.trim(); // Try numeric first. if let Ok(n) = s.parse::<i64>() { if (0..=4).contains(&n) { @@ -1176,6 +1177,13 @@ mod tests { assert_eq!(parse_priority("LOW").unwrap(), 4); } + #[test] + fn parse_priority_trims_whitespace() { + assert_eq!(parse_priority(" urgent ").unwrap(), 1); + assert_eq!(parse_priority(" 2 ").unwrap(), 2); + assert!(parse_priority(" ").is_err()); + } + #[test] fn parse_priority_rejects_invalid() { assert!(parse_priority("bogus").is_err()); diff --git a/crates/lineark/src/commands/projects.rs b/crates/lineark/src/commands/projects.rs index d83f4eb..5d793a4 100644 --- a/crates/lineark/src/commands/projects.rs +++ b/crates/lineark/src/commands/projects.rs @@ -10,6 +10,7 @@ use tabled::Tabled; use super::helpers::{ resolve_project_id, resolve_team_ids, resolve_user_id_or_me, resolve_user_ids_or_me, }; +use super::issues::parse_priority; use crate::output::{self, Format}; /// Manage projects. @@ -68,8 +69,8 @@ pub enum ProjectsAction { /// Planned target/completion date (YYYY-MM-DD). #[arg(long)] target_date: Option<String>, - /// Priority: 0=none, 1=urgent, 2=high, 3=medium, 4=low. - #[arg(short = 'p', long, value_parser = clap::value_parser!(i64).range(0..=4))] + /// Priority: 0-4 or none, urgent, high, medium, low. + #[arg(short = 'p', long, value_parser = parse_priority)] priority: Option<i64>, /// Markdown content for the project. #[arg(long)] From f4a906043ef3572fd5bdcf5c09cdab75ad499270 Mon Sep 17 00:00:00 2001 From: Cadu <cadu.coelho@gmail.com> Date: Sun, 8 Mar 2026 19:22:00 -0300 Subject: [PATCH 3/5] refactor: move parse_priority to helpers module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's a resolver like the other helpers — belongs alongside resolve_team_id, resolve_issue_id, etc. --- crates/lineark/src/commands/helpers.rs | 24 +++++++++++++++++++++ crates/lineark/src/commands/issues.rs | 28 ++----------------------- crates/lineark/src/commands/projects.rs | 4 ++-- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/crates/lineark/src/commands/helpers.rs b/crates/lineark/src/commands/helpers.rs index 657cd61..b40ac41 100644 --- a/crates/lineark/src/commands/helpers.rs +++ b/crates/lineark/src/commands/helpers.rs @@ -409,3 +409,27 @@ pub async fn resolve_cycle_id( available.join(", ") )) } + +/// Parse a priority value from either a number (0-4) or a name (none, urgent, high, medium/normal, low). +pub fn parse_priority(s: &str) -> Result<i64, String> { + let s = s.trim(); + // Try numeric first. + if let Ok(n) = s.parse::<i64>() { + if (0..=4).contains(&n) { + return Ok(n); + } + return Err(format!( + "invalid priority '{n}': valid values are 0-4 or none, urgent, high, medium, low" + )); + } + match s.to_ascii_lowercase().as_str() { + "none" => Ok(0), + "urgent" => Ok(1), + "high" => Ok(2), + "medium" | "normal" => Ok(3), + "low" => Ok(4), + _ => Err(format!( + "invalid priority '{s}': valid values are 0-4 or none, urgent, high, medium, low" + )), + } +} diff --git a/crates/lineark/src/commands/issues.rs b/crates/lineark/src/commands/issues.rs index 4ad2952..1d28285 100644 --- a/crates/lineark/src/commands/issues.rs +++ b/crates/lineark/src/commands/issues.rs @@ -11,35 +11,11 @@ use serde::{Deserialize, Serialize}; use tabled::Tabled; use super::helpers::{ - resolve_cycle_id, resolve_issue_id, resolve_label_ids, resolve_project_id, resolve_team_id, - resolve_user_id_or_me, + parse_priority, resolve_cycle_id, resolve_issue_id, resolve_label_ids, resolve_project_id, + resolve_team_id, resolve_user_id_or_me, }; use crate::output::{self, Format}; -/// Parse a priority value from either a number (0-4) or a name (none, urgent, high, medium/normal, low). -pub(crate) fn parse_priority(s: &str) -> Result<i64, String> { - let s = s.trim(); - // Try numeric first. - if let Ok(n) = s.parse::<i64>() { - if (0..=4).contains(&n) { - return Ok(n); - } - return Err(format!( - "invalid priority '{n}': valid values are 0-4 or none, urgent, high, medium, low" - )); - } - match s.to_ascii_lowercase().as_str() { - "none" => Ok(0), - "urgent" => Ok(1), - "high" => Ok(2), - "medium" | "normal" => Ok(3), - "low" => Ok(4), - _ => Err(format!( - "invalid priority '{s}': valid values are 0-4 or none, urgent, high, medium, low" - )), - } -} - /// Manage issues. #[derive(Debug, Args)] pub struct IssuesCmd { diff --git a/crates/lineark/src/commands/projects.rs b/crates/lineark/src/commands/projects.rs index 5d793a4..7a20336 100644 --- a/crates/lineark/src/commands/projects.rs +++ b/crates/lineark/src/commands/projects.rs @@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize}; use tabled::Tabled; use super::helpers::{ - resolve_project_id, resolve_team_ids, resolve_user_id_or_me, resolve_user_ids_or_me, + parse_priority, resolve_project_id, resolve_team_ids, resolve_user_id_or_me, + resolve_user_ids_or_me, }; -use super::issues::parse_priority; use crate::output::{self, Format}; /// Manage projects. From 857af61970a6c151228f98fda5727c8230fcc08e Mon Sep 17 00:00:00 2001 From: Cadu <cadu.coelho@gmail.com> Date: Sun, 8 Mar 2026 19:24:26 -0300 Subject: [PATCH 4/5] docs: document parse_priority mapping with schema reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The priority number-to-name mapping comes from Linear's GraphQL schema (IssueCreateInput.priority / IssueUpdateInput.priority). Note the schema/API discrepancy: schema says "Normal" but priorityLabel returns "Medium" for priority 3 — we accept both. --- crates/lineark/src/commands/helpers.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/lineark/src/commands/helpers.rs b/crates/lineark/src/commands/helpers.rs index b40ac41..21c6f8c 100644 --- a/crates/lineark/src/commands/helpers.rs +++ b/crates/lineark/src/commands/helpers.rs @@ -410,10 +410,23 @@ pub async fn resolve_cycle_id( )) } -/// Parse a priority value from either a number (0-4) or a name (none, urgent, high, medium/normal, low). +/// Parse a priority value from either a number (0-4) or a name. +/// +/// Mapping defined by the Linear GraphQL schema on [`IssueCreateInput.priority`] and +/// [`IssueUpdateInput.priority`] (`schema/schema.graphql`): +/// +/// | Value | Schema label | `priorityLabel` from API | +/// |-------|-------------|--------------------------| +/// | 0 | No priority | No priority | +/// | 1 | Urgent | Urgent | +/// | 2 | High | High | +/// | 3 | Normal | Medium | +/// | 4 | Low | Low | +/// +/// Note: the schema says "Normal" but the API's `priorityLabel` field returns "Medium" +/// for priority 3. We accept both `"medium"` and `"normal"` as input. pub fn parse_priority(s: &str) -> Result<i64, String> { let s = s.trim(); - // Try numeric first. if let Ok(n) = s.parse::<i64>() { if (0..=4).contains(&n) { return Ok(n); From 89655be26e4f0d1efaf78a709b6840febac9d352 Mon Sep 17 00:00:00 2001 From: Cadu <cadu.coelho@gmail.com> Date: Sun, 8 Mar 2026 19:29:30 -0300 Subject: [PATCH 5/5] fix: remove intra-doc link brackets to fix rustdoc lint The bracketed references were parsed as intra-doc links to types in a different crate, causing unresolved link errors with -Dwarnings. --- crates/lineark/src/commands/helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/lineark/src/commands/helpers.rs b/crates/lineark/src/commands/helpers.rs index 21c6f8c..8a14a9a 100644 --- a/crates/lineark/src/commands/helpers.rs +++ b/crates/lineark/src/commands/helpers.rs @@ -412,8 +412,8 @@ pub async fn resolve_cycle_id( /// Parse a priority value from either a number (0-4) or a name. /// -/// Mapping defined by the Linear GraphQL schema on [`IssueCreateInput.priority`] and -/// [`IssueUpdateInput.priority`] (`schema/schema.graphql`): +/// Mapping defined by the Linear GraphQL schema on `IssueCreateInput.priority` and +/// `IssueUpdateInput.priority` (see `schema/schema.graphql`): /// /// | Value | Schema label | `priorityLabel` from API | /// |-------|-------------|--------------------------|