codeberg_cli/actions/milestone/
create.rs

1use crate::render::ui::multi_fuzzy_select_with_key;
2use crate::types::context::BergContext;
3use crate::{actions::GeneralArgs, types::git::OwnerRepo};
4use forgejo_api::structs::CreateMilestoneOption;
5use strum::{Display, VariantArray};
6
7use crate::actions::text_manipulation::input_prompt_for;
8
9use clap::Parser;
10
11/// Create an issue
12#[derive(Parser, Debug)]
13pub struct CreateMilestoneArgs {
14    /// Title or summary
15    #[arg(short, long)]
16    pub title: Option<String>,
17
18    /// Main description of milestone
19    #[arg(short, long)]
20    pub description: Option<String>,
21}
22
23#[derive(Display, PartialEq, Eq, VariantArray)]
24enum CreatableFields {
25    Title,
26    Description,
27}
28
29impl CreateMilestoneArgs {
30    pub async fn run(self, general_args: GeneralArgs) -> anyhow::Result<()> {
31        let _ = general_args;
32        let ctx = BergContext::new(self, general_args).await?;
33
34        let OwnerRepo { owner, repo } = ctx.owner_repo()?;
35        let options = create_options(&ctx).await?;
36        let milestone = ctx
37            .client
38            .issue_create_milestone(owner.as_str(), repo.as_str(), options)
39            .await?;
40        tracing::debug!("{milestone:?}");
41        Ok(())
42    }
43}
44
45async fn create_options(
46    ctx: &BergContext<CreateMilestoneArgs>,
47) -> anyhow::Result<CreateMilestoneOption> {
48    let mut options = CreateMilestoneOption {
49        description: None,
50        due_on: None,
51        state: None,
52        title: None,
53    };
54
55    let optional_data = {
56        use CreatableFields::*;
57        [
58            (Title, ctx.args.title.is_none()),
59            (Description, ctx.args.description.is_none()),
60        ]
61        .into_iter()
62        .filter_map(|(name, missing)| missing.then_some(name))
63        .collect::<Vec<_>>()
64    };
65
66    let chosen_optionals = multi_fuzzy_select_with_key(
67        &optional_data,
68        "Choose optional properties",
69        |_| false,
70        |o| o.to_string(),
71    )?;
72
73    {
74        use CreatableFields::*;
75        options.title = milestone_title(ctx, chosen_optionals.contains(&&Title))?;
76        options.description = milestone_description(ctx, chosen_optionals.contains(&&Description))?;
77    }
78
79    Ok(options)
80}
81
82fn milestone_title(
83    ctx: &BergContext<CreateMilestoneArgs>,
84    interactive: bool,
85) -> anyhow::Result<Option<String>> {
86    let title = match ctx.args.title.as_ref() {
87        Some(title) => title.clone(),
88        None => {
89            if !interactive {
90                return Ok(None);
91            }
92            inquire::Text::new(input_prompt_for("Label Name").as_str()).prompt()?
93        }
94    };
95    Ok(Some(title))
96}
97
98fn milestone_description(
99    ctx: &BergContext<CreateMilestoneArgs>,
100    interactive: bool,
101) -> anyhow::Result<Option<String>> {
102    let description = match ctx.args.description.as_ref() {
103        Some(desc) => desc.clone(),
104        None => {
105            if !interactive {
106                return Ok(None);
107            }
108            ctx.editor_for("a description", "Enter a milestone description")?
109        }
110    };
111    Ok(Some(description))
112}