codeberg-cli 0.4.9

CLI Tool for codeberg similar to gh and glab
Documentation
use 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;

/// View details of selected milestone
#[derive(Parser, Debug)]
pub struct ViewMilestonesArgs {
    /// Select from milestones with the chosen state
    #[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, general_args).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().is_some_and(|issue_milestone| {
                issue_milestone
                    .id
                    .is_some_and(|id| milestone.id == Some(id))
            })
        })
        .map(|issue| {
            format!(
                "#{}{}",
                option_display(&issue.number),
                // TODO: Adjust
                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}", table = table.show());

    Ok(())
}