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