codeberg_cli/actions/milestone/
view.rs

1use forgejo_api::structs::{
2    IssueGetMilestonesListQuery, IssueListIssuesQuery, Milestone, StateType,
3};
4
5use crate::actions::GeneralArgs;
6use crate::render::datetime::render_datetime_regular;
7use crate::render::json::JsonToStdout;
8use crate::render::option::{option_debug_display, option_display};
9use crate::render::spinner::spin_until_ready;
10use crate::render::ui::fuzzy_select_with_key;
11
12use crate::actions::text_manipulation::select_prompt_for;
13use crate::types::api::state_type::ViewStateType;
14use crate::types::context::BergContext;
15use crate::types::git::OwnerRepo;
16use crate::types::output::OutputMode;
17
18use super::display_milestone;
19
20use clap::Parser;
21
22/// View details of selected milestone
23#[derive(Parser, Debug)]
24pub struct ViewMilestonesArgs {
25    /// Select from milestones with the chosen state
26    #[arg(short, long, value_enum, default_value_t = ViewStateType::All)]
27    pub state: ViewStateType,
28}
29
30impl ViewMilestonesArgs {
31    pub async fn run(self, general_args: GeneralArgs) -> anyhow::Result<()> {
32        let ctx = BergContext::new(self, general_args).await?;
33
34        let OwnerRepo { repo, owner } = ctx.owner_repo()?;
35        let (_, milestones_list) = spin_until_ready(ctx.client.issue_get_milestones_list(
36            owner.as_str(),
37            repo.as_str(),
38            IssueGetMilestonesListQuery::default(),
39        ))
40        .await?;
41
42        let selected_milestone = fuzzy_select_with_key(
43            &milestones_list,
44            select_prompt_for("milestone"),
45            display_milestone,
46        )?;
47
48        match general_args.output_mode {
49            OutputMode::Pretty => {
50                present_milestone_overview(&ctx, selected_milestone).await?;
51            }
52            OutputMode::Json => selected_milestone.print_json()?,
53        }
54
55        Ok(())
56    }
57}
58
59async fn present_milestone_overview(
60    ctx: &BergContext<ViewMilestonesArgs>,
61    milestone: &Milestone,
62) -> anyhow::Result<()> {
63    let OwnerRepo { repo, owner } = ctx.owner_repo()?;
64    let (_, issues_list) = spin_until_ready(
65        ctx.client.issue_list_issues(
66            owner.as_str(),
67            repo.as_str(),
68            IssueListIssuesQuery {
69                milestones: milestone
70                    .id
71                    .as_ref()
72                    .map(|id| id.to_string())
73                    .or_else(|| milestone.title.clone()),
74                ..Default::default()
75            },
76        ),
77    )
78    .await?;
79
80    let mut milestone_issues = issues_list
81        .iter()
82        .filter(|&issue| {
83            issue.milestone.as_ref().is_some_and(|issue_milestone| {
84                issue_milestone
85                    .id
86                    .is_some_and(|id| milestone.id == Some(id))
87            })
88        })
89        .map(|issue| {
90            format!(
91                "#{}{}",
92                option_display(&issue.number),
93                // TODO: Adjust
94                if issue.state == Some(StateType::Closed) {
95                    "✓ "
96                } else {
97                    "○ "
98                }
99            )
100        })
101        .collect::<Vec<_>>();
102
103    milestone_issues.sort();
104
105    let mut table = ctx.make_table();
106
107    table
108        .set_header(vec![format!(
109            "Milestone #{}",
110            option_display(&milestone.id)
111        )])
112        .add_row(vec![String::from("Name"), option_display(&milestone.title)])
113        .add_row(vec![
114            String::from("Status"),
115            option_debug_display(&milestone.state),
116        ])
117        .add_row(vec![
118            String::from("Description"),
119            option_display(&milestone.description),
120        ]);
121
122    if !milestone_issues.is_empty() {
123        table.add_row(vec![
124            String::from("Related Issues"),
125            milestone_issues.join(", "),
126        ]);
127    }
128
129    table
130        .add_row(vec![
131            String::from("Due On"),
132            option_display(&milestone.due_on.as_ref().map(render_datetime_regular)),
133        ])
134        .add_row(vec![
135            String::from("Progress"),
136            format!(
137                "Progress: {} / {} done",
138                option_display(&milestone.closed_issues),
139                milestone.open_issues.unwrap_or_default()
140                    + milestone.closed_issues.unwrap_or_default()
141            ),
142        ]);
143
144    println!("{table}", table = table.show());
145
146    Ok(())
147}