//! Issues: list, read, comment, open and close.
use anyhow::Result;
use crate::cli::IssueCommand;
use crate::commands::{Ctx, read_body};
use crate::output::{
Table, ago, author_of, bold, heading, indented, labels, note, state_cell, truncate,
};
/// "No open issues" reads; "No all issues" doesn't.
fn qualifier(state: &str) -> String {
if state == "all" {
String::new()
} else {
format!("{state} ")
}
}
pub async fn run(ctx: &Ctx, command: &IssueCommand) -> Result<()> {
let repo = ctx.repo()?;
match command {
IssueCommand::List {
state,
search,
limit,
} => {
let res = ctx
.api
.issues(&repo, Some(state.as_param()), search.as_deref(), *limit)
.await?;
if ctx.emitted(&res.raw)? {
return Ok(());
}
if res.issues.is_empty() {
note(&format!(
"No {}issues in {repo}.",
qualifier(state.as_param())
));
return Ok(());
}
let mut table = Table::new(["#", "STATE", "TITLE", "AUTHOR", "LABELS", "UPDATED"]);
for issue in &res.issues {
table.row([
format!("#{}", issue.number),
state_cell(&issue.state),
truncate(issue.title.as_deref().unwrap_or("(untitled)"), 52),
author_of(issue.author.as_ref()),
truncate(&labels(&issue.labels), 24),
ago(issue.updated_at.as_deref().or(issue.inserted_at.as_deref())),
]);
}
table.print();
Ok(())
}
IssueCommand::Show {
number,
no_comments,
} => {
let res = ctx.api.issue(&repo, *number).await?;
if ctx.emitted(&res.raw)? {
return Ok(());
}
let issue = &res.issue;
heading(&format!(
"#{} {}",
issue.number,
issue.title.as_deref().unwrap_or("(untitled)")
));
println!(
"{} · opened by {} {}{}",
state_cell(&issue.state),
author_of(issue.author.as_ref()),
ago(issue.inserted_at.as_deref()),
if issue.labels.is_empty() {
String::new()
} else {
format!(" · {}", labels(&issue.labels))
}
);
if let Some(body) = issue.body.as_deref().filter(|b| !b.trim().is_empty()) {
println!();
indented(body, " ");
}
if !no_comments && !res.comments.is_empty() {
println!("\n{}", bold(&format!("{} comment(s)", res.comments.len())));
for comment in &res.comments {
println!();
println!(
"{} {}{}",
bold(&author_of(comment.author.as_ref())),
ago(comment.inserted_at.as_deref()),
if comment.edited_at.is_some() {
" (edited)"
} else {
""
}
);
let body = if comment.deleted {
"(deleted)"
} else {
comment.body.as_deref().unwrap_or("")
};
indented(body, " ");
}
}
note(&format!(
"\n{}",
ctx.repo_web_url(&repo, &format!("/issues/{}", issue.number))
));
if !res.can_comment {
note("(read-only: this token can't comment here)");
}
Ok(())
}
IssueCommand::Create {
title,
body,
labels,
} => {
let text = read_body(body.body.as_deref(), body.body_file.as_deref(), false)?;
let res = ctx
.api
.create_issue(
&repo,
title,
text.as_deref().map(str::trim).filter(|t| !t.is_empty()),
labels,
)
.await?;
if ctx.emitted(&res.raw)? {
return Ok(());
}
println!(
"Opened issue #{}: {}",
res.issue.number,
res.issue.title.as_deref().unwrap_or("(untitled)")
);
println!(
"{}",
ctx.repo_web_url(&repo, &format!("/issues/{}", res.issue.number))
);
Ok(())
}
IssueCommand::Comment { number, body } => {
let text = read_body(body.body.as_deref(), body.body_file.as_deref(), true)?
.expect("required body");
let res = ctx
.api
.comment_on_issue(&repo, *number, text.trim())
.await?;
if ctx.emitted(&res.raw)? {
return Ok(());
}
println!("Commented on issue #{number}.");
Ok(())
}
IssueCommand::Close { number } => set_state(ctx, &repo, *number, "closed").await,
IssueCommand::Reopen { number } => set_state(ctx, &repo, *number, "open").await,
}
}
async fn set_state(ctx: &Ctx, repo: &crate::api::RepoRef, number: i64, state: &str) -> Result<()> {
let res = ctx.api.set_issue_state(repo, number, state).await?;
if ctx.emitted(&res.raw)? {
return Ok(());
}
println!("Issue #{number} is now {}.", state_cell(state));
Ok(())
}
neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
5.7 KiB · text
History
16cf1ed