codeberg_cli/actions/milestone/
view.rsuse forgejo_api::structs::{
IssueGetMilestonesListQuery, IssueListIssuesQuery, Milestone, StateType,
};
use crate::actions::GeneralArgs;
use crate::render::datetime::render_datetime_regular;
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::actions::text_manipulation::select_prompt_for;
use crate::types::api::state_type::ViewStateType;
use crate::types::context::BergContext;
use crate::types::git::OwnerRepo;
use super::display_milestone;
use clap::Parser;
#[derive(Parser, Debug)]
pub struct ViewMilestonesArgs {
#[arg(short, long, value_enum, default_value_t = ViewStateType::All)]
pub state: ViewStateType,
}
impl ViewMilestonesArgs {
pub async fn run(self, general_args: GeneralArgs) -> anyhow::Result<()> {
let _ = general_args;
let ctx = BergContext::new(self).await?;
let OwnerRepo { repo, owner } = ctx.owner_repo()?;
let milestones_list = spin_until_ready(ctx.client.issue_get_milestones_list(
owner.as_str(),
repo.as_str(),
IssueGetMilestonesListQuery::default(),
))
.await?;
let selected_milestone = fuzzy_select_with_key(
&milestones_list,
select_prompt_for("milestone"),
display_milestone,
)?;
present_milestone_overview(&ctx, selected_milestone).await?;
Ok(())
}
}
async fn present_milestone_overview(
ctx: &BergContext<ViewMilestonesArgs>,
milestone: &Milestone,
) -> anyhow::Result<()> {
let OwnerRepo { repo, owner } = ctx.owner_repo()?;
let issues_list = spin_until_ready(
ctx.client.issue_list_issues(
owner.as_str(),
repo.as_str(),
IssueListIssuesQuery {
milestones: milestone
.id
.as_ref()
.map(|id| id.to_string())
.or_else(|| milestone.title.clone()),
..Default::default()
},
),
)
.await?;
let mut milestone_issues = issues_list
.iter()
.filter(|&issue| {
issue.milestone.as_ref().map_or(false, |issue_milestone| {
issue_milestone
.id
.is_some_and(|id| milestone.id == Some(id))
})
})
.map(|issue| {
format!(
"#{}{}",
option_display(&issue.number),
if issue.state == Some(StateType::Closed) {
"✓ "
} else {
"○ "
}
)
})
.collect::<Vec<_>>();
milestone_issues.sort();
let mut table = ctx.make_table();
table
.set_header(vec![format!(
"Milestone #{}",
option_display(&milestone.id)
)])
.add_row(vec![String::from("Name"), option_display(&milestone.title)])
.add_row(vec![
String::from("Status"),
option_debug_display(&milestone.state),
])
.add_row(vec![
String::from("Description"),
option_display(&milestone.description),
]);
if !milestone_issues.is_empty() {
table.add_row(vec![
String::from("Related Issues"),
milestone_issues.join(", "),
]);
}
table
.add_row(vec![
String::from("Due On"),
option_display(&milestone.due_on.as_ref().map(render_datetime_regular)),
])
.add_row(vec![
String::from("Progress"),
format!(
"Progress: {} / {} done",
option_display(&milestone.closed_issues),
milestone.open_issues.unwrap_or_default()
+ milestone.closed_issues.unwrap_or_default()
),
]);
println!("{table}");
Ok(())
}