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