codeberg_cli/actions/label/
edit.rs

1use crate::actions::GeneralArgs;
2use crate::actions::label::display_label;
3
4use crate::render::color::mk_color_validator;
5use crate::render::json::JsonToStdout;
6use crate::render::spinner::spin_until_ready;
7use crate::render::ui::{fuzzy_select_with_key, multi_fuzzy_select_with_key};
8use crate::types::context::BergContext;
9use crate::types::git::OwnerRepo;
10use crate::types::output::OutputMode;
11use anyhow::Context;
12use forgejo_api::structs::{EditLabelOption, IssueListLabelsQuery, Label};
13use strum::{Display, VariantArray};
14
15use crate::actions::text_manipulation::{input_prompt_for, select_prompt_for};
16
17use clap::Parser;
18
19/// Edit selected label
20#[derive(Parser, Debug)]
21pub struct EditLabelArgs {}
22
23#[derive(Display, PartialEq, Eq, VariantArray)]
24enum EditableFields {
25    Name,
26    Description,
27    Color,
28}
29
30impl EditLabelArgs {
31    pub async fn run(self, general_args: GeneralArgs) -> anyhow::Result<()> {
32        let ctx = BergContext::new(self, general_args).await?;
33
34        let OwnerRepo { repo, owner } = ctx.owner_repo()?;
35        let label = select_label(&ctx).await?;
36        let label_id = label.id.context("Selected label doesn't have an ID")?;
37
38        let options = create_options(&ctx, &label).await?;
39
40        let updated_label = ctx
41            .client
42            .issue_edit_label(owner.as_str(), repo.as_str(), label_id as u64, options)
43            .await?;
44
45        match general_args.output_mode {
46            OutputMode::Pretty => {
47                tracing::debug!("{updated_label:?}");
48            }
49            OutputMode::Json => updated_label.print_json()?,
50        }
51
52        Ok(())
53    }
54}
55
56async fn select_label(ctx: &BergContext<EditLabelArgs>) -> anyhow::Result<Label> {
57    let OwnerRepo { repo, owner } = ctx.owner_repo()?;
58    let (_, labels_list) = spin_until_ready(ctx.client.issue_list_labels(
59        owner.as_str(),
60        repo.as_str(),
61        IssueListLabelsQuery::default(),
62    ))
63    .await?;
64
65    if labels_list.is_empty() {
66        anyhow::bail!("No labels found in this repository");
67    }
68
69    fuzzy_select_with_key(&labels_list, select_prompt_for("label"), display_label).cloned()
70}
71
72async fn create_options(
73    ctx: &BergContext<EditLabelArgs>,
74    label: &Label,
75) -> anyhow::Result<EditLabelOption> {
76    let selected_update_fields = multi_fuzzy_select_with_key(
77        EditableFields::VARIANTS,
78        select_prompt_for("options"),
79        |_| false,
80        |f| f.to_string(),
81    )?;
82
83    let mut options = EditLabelOption {
84        color: None,
85        description: None,
86        exclusive: None,
87        is_archived: None,
88        name: None,
89    };
90
91    if selected_update_fields.contains(&&EditableFields::Name) {
92        let current_name = label.name.as_ref().cloned();
93        options.name.replace(label_name(ctx, current_name).await?);
94    }
95    if selected_update_fields.contains(&&EditableFields::Description) {
96        let current_description = label.description.as_ref().cloned();
97        options
98            .description
99            .replace(label_description(ctx, current_description).await?);
100    }
101    if selected_update_fields.contains(&&EditableFields::Color) {
102        let current_color = label.color.as_ref().cloned();
103        options
104            .color
105            .replace(label_color(ctx, current_color).await?);
106    }
107
108    Ok(options)
109}
110
111async fn label_name(
112    ctx: &BergContext<EditLabelArgs>,
113    current_name: Option<String>,
114) -> anyhow::Result<String> {
115    ctx.editor_for(
116        "Choose a new label name",
117        current_name.as_deref().unwrap_or("Enter a label name"),
118    )
119}
120
121async fn label_description(
122    ctx: &BergContext<EditLabelArgs>,
123    current_description: Option<String>,
124) -> anyhow::Result<String> {
125    ctx.editor_for(
126        "a description",
127        current_description
128            .as_deref()
129            .unwrap_or("Enter a label description"),
130    )
131}
132
133async fn label_color(
134    _ctx: &BergContext<EditLabelArgs>,
135    current_color: Option<String>,
136) -> anyhow::Result<String> {
137    mk_color_validator(
138        inquire::Text::new(input_prompt_for("Enter a color").as_str())
139            .with_default(current_color.as_deref().unwrap_or("Enter a label color")),
140    )
141    .prompt()
142    .map_err(anyhow::Error::from)
143}