codeberg_cli/actions/milestone/
create.rs1use 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#[derive(Parser, Debug)]
13pub struct CreateMilestoneArgs {
14 #[arg(short, long)]
16 pub title: Option<String>,
17
18 #[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}