codeberg_cli/actions/label/
edit.rs1use 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#[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}