diff --git a/src/cli/auth.rs b/src/cli/auth.rs index 018ac3d..d1364d7 100644 --- a/src/cli/auth.rs +++ b/src/cli/auth.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use dialoguer::{Input, Password}; use crate::api::auth; @@ -9,11 +9,13 @@ use crate::output; pub async fn login_token() -> Result<()> { let email: String = dialoguer::Input::new() .with_prompt("Jira email") - .interact_text()?; + .interact_text() + .context("failed to read Jira email")?; let token: String = dialoguer::Password::new() .with_prompt("API token") - .interact()?; + .interact() + .context("failed to read API token")?; auth::store_api_token(&email, &token)?; println!("Credentials stored in keychain."); @@ -28,11 +30,13 @@ pub async fn login_oauth() -> Result<()> { let client_id: String = Input::new() .with_prompt("OAuth Client ID") - .interact_text()?; + .interact_text() + .context("failed to read OAuth client ID")?; let client_secret: String = Password::new() .with_prompt("OAuth Client Secret") - .interact()?; + .interact() + .context("failed to read OAuth client secret")?; // Store OAuth app credentials in keychain crate::api::auth::store_oauth_app_credentials(&client_id, &client_secret)?; diff --git a/src/cli/init.rs b/src/cli/init.rs index 04560fc..8ba9e6b 100644 --- a/src/cli/init.rs +++ b/src/cli/init.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use dialoguer::{Confirm, Input, Select}; use crate::config::{ @@ -12,7 +12,8 @@ pub async fn handle() -> Result<()> { // Step 1: Instance URL let url: String = Input::new() .with_prompt("Jira instance URL (e.g., https://yourorg.atlassian.net)") - .interact_text()?; + .interact_text() + .context("failed to read Jira instance URL")?; let url = url.trim_end_matches('/').to_string(); // Step 2: Auth method @@ -21,7 +22,8 @@ pub async fn handle() -> Result<()> { .with_prompt("Authentication method") .items(&auth_methods) .default(0) - .interact()?; + .interact() + .context("failed to prompt for authentication method")?; let global = GlobalConfig { instance: InstanceConfig { @@ -58,7 +60,8 @@ pub async fn handle() -> Result<()> { let setup_project = Confirm::new() .with_prompt("Configure this directory as a Jira project?") .default(true) - .interact()?; + .interact() + .context("failed to prompt for project setup")?; if setup_project { let boards = client.list_boards(None, None).await?; @@ -72,10 +75,14 @@ pub async fn handle() -> Result<()> { let board_choice = Select::new() .with_prompt("Select board") .items(&board_names) - .interact()?; + .interact() + .context("failed to prompt for board selection")?; let selected_board = &boards[board_choice]; - let project_key: String = Input::new().with_prompt("Project key").interact_text()?; + let project_key: String = Input::new() + .with_prompt("Project key") + .interact_text() + .context("failed to read project key")?; let project_config = format!( "project = \"{}\"\nboard_id = {}\n", @@ -118,7 +125,8 @@ pub async fn handle() -> Result<()> { let selection = Select::new() .with_prompt("Multiple story points fields found. Select one") .items(&names) - .interact()?; + .interact() + .context("failed to prompt for story points field selection")?; Some(matches[selection].0.clone()) } }; diff --git a/src/cli/issue/helpers.rs b/src/cli/issue/helpers.rs index 7ca06eb..f574930 100644 --- a/src/cli/issue/helpers.rs +++ b/src/cli/issue/helpers.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use crate::api::client::JiraClient; use crate::config::Config; @@ -67,7 +67,8 @@ pub(super) async fn resolve_team_field( let selection = dialoguer::Select::new() .with_prompt(format!("Multiple teams named \"{}\"", team_name)) .items(&labels) - .interact()?; + .interact() + .context("failed to prompt for team selection")?; Ok((field_id, duplicates[selection].id.clone())) } crate::partial_match::MatchResult::Ambiguous(matches) => { @@ -82,7 +83,8 @@ pub(super) async fn resolve_team_field( let selection = dialoguer::Select::new() .with_prompt(format!("Multiple teams match \"{team_name}\"")) .items(&matches) - .interact()?; + .interact() + .context("failed to prompt for team selection")?; let selected_name = &matches[selection]; let idx = teams .iter() @@ -115,7 +117,8 @@ pub(super) fn resolve_story_points_field_id(config: &Config) -> Result { pub(super) fn prompt_input(prompt: &str) -> Result { let input: String = dialoguer::Input::new() .with_prompt(prompt) - .interact_text()?; + .interact_text() + .with_context(|| format!("failed to read {}", prompt))?; Ok(input) } @@ -197,7 +200,8 @@ fn disambiguate_user( let selection = dialoguer::Select::new() .with_prompt(format!("Multiple users named \"{}\"", name)) .items(&labels) - .interact()?; + .interact() + .context("failed to prompt for user selection")?; Ok(( duplicates[selection].account_id.clone(), duplicates[selection].display_name.clone(), @@ -214,7 +218,8 @@ fn disambiguate_user( let selection = dialoguer::Select::new() .with_prompt(format!("Multiple users match \"{name}\"")) .items(&matches) - .interact()?; + .interact() + .context("failed to prompt for user selection")?; let selected_name = &matches[selection]; let idx = users .iter() @@ -428,7 +433,8 @@ pub(super) async fn resolve_asset( let selection = dialoguer::Select::new() .with_prompt(format!("Multiple assets match \"{}\"", input)) .items(&items) - .interact()?; + .interact() + .context("failed to prompt for asset selection")?; Ok(duplicates[selection].object_key.clone()) } crate::partial_match::MatchResult::Ambiguous(matches) => { @@ -456,7 +462,8 @@ pub(super) async fn resolve_asset( let selection = dialoguer::Select::new() .with_prompt(format!("Multiple assets match \"{}\"", input)) .items(&items) - .interact()?; + .interact() + .context("failed to prompt for asset selection")?; Ok(filtered[selection].object_key.clone()) } crate::partial_match::MatchResult::None(_) => { diff --git a/src/cli/issue/links.rs b/src/cli/issue/links.rs index 02fd783..4830fb3 100644 --- a/src/cli/issue/links.rs +++ b/src/cli/issue/links.rs @@ -1,5 +1,5 @@ use super::json_output; -use anyhow::{Result, bail}; +use anyhow::{Context, Result, bail}; use crate::api::client::JiraClient; use crate::cli::{IssueCommand, OutputFormat}; @@ -74,7 +74,8 @@ pub(super) async fn handle_link( let selection = dialoguer::Select::new() .with_prompt(format!("Multiple types match \"{link_type_name}\"")) .items(&matches) - .interact()?; + .interact() + .context("failed to prompt for link type selection")?; matches[selection].clone() } MatchResult::None(_) => { @@ -143,7 +144,8 @@ pub(super) async fn handle_unlink( let selection = dialoguer::Select::new() .with_prompt(format!("Multiple types match \"{type_name}\"")) .items(&matches) - .interact()?; + .interact() + .context("failed to prompt for link type selection")?; matches[selection].clone() } MatchResult::None(_) => {