use forgejo_api::structs::{CreateIssueCommentOption, IssueListIssuesQuery};
use miette::{Context, IntoDiagnostic};
use crate::actions::issue::display_issue;
use crate::actions::GlobalArgs;
use crate::actions::text_manipulation::select_prompt_for;
use crate::render::json::JsonToStdout;
use crate::render::spinner::spin_until_ready;
use crate::render::ui::fuzzy_select_with_key;
use crate::types::context::BergContext;
use crate::types::git::OwnerRepo;
use crate::types::output::OutputMode;
use clap::Parser;
#[derive(Parser, Debug, Clone)]
pub struct CommentIssueArgs {
#[arg(short, long)]
pub number: Option<i64>,
#[arg(short, long)]
pub body: Option<String>,
}
impl CommentIssueArgs {
pub async fn run(self, global_args: GlobalArgs) -> miette::Result<()> {
let ctx = BergContext::new(self, global_args).await?;
let OwnerRepo { owner, repo } = ctx.owner_repo()?;
let (nr, options) = create_options(&ctx).await?;
let comment = ctx
.client
.issue_create_comment(owner.as_str(), repo.as_str(), nr, options)
.await
.into_diagnostic()?;
match ctx.global_args.output_mode {
OutputMode::Pretty => {
tracing::debug!("{comment:?}");
}
OutputMode::Json => {
comment.print_json()?;
}
}
Ok(())
}
}
async fn create_options(
ctx: &BergContext<CommentIssueArgs>,
) -> miette::Result<(i64, CreateIssueCommentOption)> {
let OwnerRepo { owner, repo } = ctx.owner_repo()?;
let selected_issue = if ctx.global_args.non_interactive {
None
} else {
let (_, issues_list) = spin_until_ready(
ctx.client
.issue_list_issues(
owner.as_str(),
repo.as_str(),
IssueListIssuesQuery::default(),
)
.send(),
)
.await
.into_diagnostic()?;
let selected_issue =
fuzzy_select_with_key(&issues_list, select_prompt_for("issue"), display_issue)?;
Some(selected_issue.clone())
};
let nr = match selected_issue
.as_ref()
.context("No issue selected")
.and_then(|issue| issue.number.context("Selected issue had no issue number"))
{
Ok(ok) => ok,
Err(e) => ctx
.args
.number
.context(e)
.context("You have to provide an issue number in non-interactive mode!")?,
};
let body = if ctx.global_args.non_interactive {
ctx.args
.body
.clone()
.context("You have to provide a body in non-interactive mode!")?
} else {
get_comment_input(ctx, selected_issue.and_then(|issue| issue.title.clone()))?
};
let options = CreateIssueCommentOption {
body,
updated_at: None,
};
Ok((nr, options))
}
fn get_comment_input(
ctx: &BergContext<CommentIssueArgs>,
issue_title: impl Into<Option<String>>,
) -> miette::Result<String> {
let opt_issue_title = issue_title
.into()
.map(|title| format!(" for issue \"{title}\""))
.unwrap_or_default();
ctx.editor_for(
"a comment",
format!("Write a comment{opt_issue_title}").as_str(),
)
}