use crate::render::comment::render_comment;
use crate::render::datetime::render_datetime_and_info;
use crate::render::json::JsonToStdout;
use crate::render::option::{option_debug_display, option_display};
use crate::render::spinner::spin_until_ready;
use crate::render::ui::fuzzy_select_with_key;
use crate::types::api::state_type::ViewStateType;
use crate::types::context::BergContext;
use crate::types::output::OutputMode;
use crate::{actions::GlobalArgs, types::git::OwnerRepo};
use forgejo_api::structs::{
Issue, IssueGetCommentsQuery, IssueListIssuesQuery, IssueListIssuesQueryState,
};
use miette::{Context, IntoDiagnostic};
use crate::actions::text_manipulation::select_prompt_for;
use super::display_issue;
use clap::Parser;
#[derive(Parser, Debug)]
pub struct ViewIssueArgs {
pub index: Option<i64>,
#[arg(short, long, value_enum, default_value_t = ViewStateType::All)]
pub state: ViewStateType,
#[arg(short, long)]
pub comments: bool,
}
impl ViewIssueArgs {
pub async fn run(self, global_args: GlobalArgs) -> miette::Result<()> {
let ctx = BergContext::new(self, global_args).await?;
let OwnerRepo { repo, owner } = ctx.owner_repo()?;
let state = match ctx.args.state {
ViewStateType::Closed => IssueListIssuesQueryState::Closed,
ViewStateType::Open => IssueListIssuesQueryState::Open,
ViewStateType::All => IssueListIssuesQueryState::All,
};
let selected_issue = if let Some(index) = ctx.args.index {
ctx.client
.issue_get_issue(owner.as_str(), repo.as_str(), index)
.await
.into_diagnostic()?
} else {
if ctx.global_args.non_interactive {
miette::bail!("non-interactive mode enabled. You have to specify an issue #number");
};
let (_, issues_list) = spin_until_ready(
ctx.client
.issue_list_issues(
owner.as_str(),
repo.as_str(),
IssueListIssuesQuery {
state: Some(state),
..Default::default()
},
)
.send(),
)
.await
.into_diagnostic()?;
fuzzy_select_with_key(&issues_list, select_prompt_for("issue"), display_issue)
.cloned()?
};
match ctx.global_args.output_mode {
OutputMode::Pretty => {
if ctx.args.comments {
spin_until_ready(present_issue_comments(&ctx, &selected_issue)).await?;
} else {
present_issue_overview(&ctx, &selected_issue);
}
}
OutputMode::Json => {
if ctx.args.comments {
let index = selected_issue
.number
.context("Selected issue has to have a valid issue number")?;
let (_, comments) = ctx
.client
.issue_get_comments(
owner.as_str(),
repo.as_str(),
index,
IssueGetCommentsQuery::default(),
)
.send()
.await
.into_diagnostic()?;
comments.print_json()?;
} else {
selected_issue.print_json()?;
}
}
}
Ok(())
}
}
fn present_issue_overview(ctx: &BergContext<ViewIssueArgs>, issue: &Issue) {
let days_passed_since_creation =
option_display(&issue.created_at.as_ref().map(render_datetime_and_info));
let table = ctx
.make_table()
.set_header(vec![option_display(
&issue.id.as_ref().map(|id| format!("Issue #{}", id)),
)])
.add_row(vec![String::from("Title"), option_display(&issue.title)])
.add_row(vec![String::from("Created"), days_passed_since_creation])
.add_row(vec![
String::from("Labels"),
option_display(&issue.labels.as_ref().map(|labels| {
labels
.iter()
.map(|label| option_display(&label.name))
.collect::<Vec<_>>()
.join(", ")
})),
])
.add_row(vec![
String::from("State"),
option_debug_display(&issue.state),
])
.add_row(vec![
String::from("Description"),
option_display(&issue.body),
]);
println!("{table}", table = table.show());
}
async fn present_issue_comments(
ctx: &BergContext<ViewIssueArgs>,
issue: &Issue,
) -> miette::Result<()> {
let index = issue.number.context("Issue has no issue number")?;
let OwnerRepo { repo, owner } = ctx.owner_repo()?;
let (header, comments) = {
let (_, comments_list) = ctx
.client
.issue_get_comments(
owner.as_str(),
repo.as_str(),
index,
IssueGetCommentsQuery::default(),
)
.send()
.await
.into_diagnostic()?;
let header = format!(
"Issue #{} {}",
index,
if comments_list.is_empty() {
"(no comments)"
} else {
"comments"
}
);
(header, comments_list)
};
let table = ctx
.make_table()
.add_row(vec![header])
.add_rows(comments.into_iter().filter_map(|comment| {
let username = comment.user.as_ref()?.login.as_ref()?.as_str();
let creation_time = comment.created_at.as_ref()?;
let comment = comment.body.as_ref()?;
let comment = render_comment(username, creation_time, comment);
Some(vec![comment])
}));
println!("{table}", table = table.show());
Ok(())
}