codeberg_cli/actions/label/
edit.rs

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