codeberg_cli/actions/label/
create.rs

1use 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/// Create a label
15#[derive(Parser, Debug)]
16pub struct CreateLabelArgs {
17    /// Label name
18    #[arg(short, long)]
19    pub name: Option<String>,
20
21    /// Label color (in hex format "#abcdef")
22    #[arg(short, long)]
23    pub color: Option<String>,
24
25    /// Label purpose description
26    #[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}