codeberg_cli/actions/label/
create.rs1use crate::render::color::mk_color_validator;
2use crate::{actions::GeneralArgs, types::git::OwnerRepo};
3
4use crate::render::ui::multi_fuzzy_select_with_key;
5use crate::types::context::BergContext;
6use forgejo_api::structs::CreateLabelOption;
7use strum::*;
8
9use crate::actions::text_manipulation::input_prompt_for;
10
11use clap::Parser;
12
13#[derive(Parser, Debug)]
15pub struct CreateLabelArgs {
16 #[arg(short, long)]
18 pub name: Option<String>,
19
20 #[arg(short, long)]
22 pub color: Option<String>,
23
24 #[arg(short, long)]
26 pub description: Option<String>,
27}
28
29#[derive(Display, PartialEq, Eq, VariantArray)]
30enum CreatableFields {
31 Description,
32 Color,
33}
34
35impl CreateLabelArgs {
36 pub async fn run(self, general_args: GeneralArgs) -> anyhow::Result<()> {
37 let _ = general_args;
38 let ctx = BergContext::new(self, general_args).await?;
39
40 let OwnerRepo { repo, owner } = ctx.owner_repo()?;
41 let options = create_options(&ctx).await?;
42 let label = ctx
43 .client
44 .issue_create_label(owner.as_str(), repo.as_str(), options)
45 .await?;
46 tracing::debug!("{label:?}");
47 Ok(())
48 }
49}
50
51async fn create_options(ctx: &BergContext<CreateLabelArgs>) -> anyhow::Result<CreateLabelOption> {
52 let name = match ctx.args.name.as_ref().cloned() {
53 Some(name) => name,
54 None => inquire::Text::new(input_prompt_for("Label Name").as_str()).prompt()?,
55 };
56
57 let color = ctx
58 .args
59 .color
60 .as_ref()
61 .cloned()
62 .unwrap_or(String::from("#ffffff"));
63
64 let mut options = CreateLabelOption {
65 name,
66 color,
67 description: None,
68 exclusive: None,
69 is_archived: None,
70 };
71
72 let optional_data = {
73 use CreatableFields::*;
74 [
75 (Description, ctx.args.description.is_none()),
76 (Color, ctx.args.color.is_none()),
77 ]
78 .into_iter()
79 .filter_map(|(name, missing)| missing.then_some(name))
80 .collect::<Vec<_>>()
81 };
82
83 let chosen_optionals = multi_fuzzy_select_with_key(
84 &optional_data,
85 "Choose optional properties",
86 |_| false,
87 |o| o.to_string(),
88 )?;
89
90 {
91 use CreatableFields::*;
92 options.description = label_description(ctx, chosen_optionals.contains(&&Description))?;
93 if let Some(color) = label_color(ctx, chosen_optionals.contains(&&Color))? {
94 options.color = color;
95 }
96 }
97
98 Ok(options)
99}
100
101fn label_description(
102 ctx: &BergContext<CreateLabelArgs>,
103 interactive: bool,
104) -> anyhow::Result<Option<String>> {
105 let description = match ctx.args.description.as_ref() {
106 Some(desc) => desc.clone(),
107 None => {
108 if !interactive {
109 return Ok(None);
110 }
111 ctx.editor_for("a description", "Enter an issue description")?
112 }
113 };
114 Ok(Some(description))
115}
116
117fn label_color(
118 ctx: &BergContext<CreateLabelArgs>,
119 interactive: bool,
120) -> anyhow::Result<Option<String>> {
121 let color = match ctx.args.color.as_ref() {
122 Some(color) => color.clone(),
123 None => {
124 if !interactive {
125 return Ok(None);
126 }
127 mk_color_validator(inquire::Text::new(
128 input_prompt_for("Enter a color").as_str(),
129 ))
130 .prompt()?
131 }
132 };
133 Ok(Some(color))
134}