use crate::render::color::mk_color_validator;
use crate::render::json::JsonToStdout;
use crate::{actions::GlobalArgs, types::git::OwnerRepo};
use crate::render::ui::multi_fuzzy_select_with_key;
use crate::types::context::BergContext;
use forgejo_api::structs::CreateLabelOption;
use miette::IntoDiagnostic;
use strum::*;
use crate::actions::text_manipulation::input_prompt_for;
use clap::Parser;
#[derive(Parser, Debug)]
pub struct CreateLabelArgs {
#[arg(short, long)]
pub name: Option<String>,
#[arg(short, long)]
pub color: Option<String>,
#[arg(short, long)]
pub description: Option<String>,
}
#[derive(Display, PartialEq, Eq, VariantArray)]
enum CreatableFields {
Description,
Color,
}
impl CreateLabelArgs {
pub async fn run(self, global_args: GlobalArgs) -> miette::Result<()> {
let ctx = BergContext::new(self, global_args).await?;
let OwnerRepo { repo, owner } = ctx.owner_repo()?;
let options = create_options(&ctx).await?;
let label = ctx
.client
.issue_create_label(owner.as_str(), repo.as_str(), options)
.await
.into_diagnostic()?;
match ctx.global_args.output_mode {
crate::types::output::OutputMode::Pretty => {
tracing::debug!("{label:?}");
}
crate::types::output::OutputMode::Json => label.print_json()?,
}
Ok(())
}
}
async fn create_options(ctx: &BergContext<CreateLabelArgs>) -> miette::Result<CreateLabelOption> {
let name = match ctx.args.name.as_ref().cloned() {
Some(name) => name,
None => {
if ctx.global_args.non_interactive {
miette::bail!("You need to specify a name in non-interactive mode!");
}
inquire::Text::new(input_prompt_for("Label Name").as_str())
.prompt()
.into_diagnostic()?
}
};
let color = ctx
.args
.color
.as_ref()
.cloned()
.unwrap_or(String::from("#ffffff"));
let mut options = CreateLabelOption {
name,
color,
description: ctx.args.description.clone(),
exclusive: None,
is_archived: None,
};
if !ctx.global_args.non_interactive {
let optional_data = {
use CreatableFields::*;
[
(Description, ctx.args.description.is_none()),
(Color, ctx.args.color.is_none()),
]
.into_iter()
.filter_map(|(name, missing)| missing.then_some(name))
.collect::<Vec<_>>()
};
let chosen_optionals = multi_fuzzy_select_with_key(
&optional_data,
"Choose optional properties",
|_| false,
|o| o.to_string(),
)?;
{
use CreatableFields::*;
options.description = label_description(ctx, chosen_optionals.contains(&&Description))?;
if let Some(color) = label_color(ctx, chosen_optionals.contains(&&Color))? {
options.color = color;
}
}
}
Ok(options)
}
fn label_description(
ctx: &BergContext<CreateLabelArgs>,
interactive: bool,
) -> miette::Result<Option<String>> {
let description = match ctx.args.description.as_ref() {
Some(desc) => desc.clone(),
None => {
if !interactive {
return Ok(None);
}
ctx.editor_for("a description", "Enter an issue description")?
}
};
Ok(Some(description))
}
fn label_color(
ctx: &BergContext<CreateLabelArgs>,
interactive: bool,
) -> miette::Result<Option<String>> {
let color = match ctx.args.color.as_ref() {
Some(color) => color.clone(),
None => {
if !interactive {
return Ok(None);
}
mk_color_validator(inquire::Text::new(
input_prompt_for("Enter a color").as_str(),
))
.prompt()
.into_diagnostic()?
}
};
Ok(Some(color))
}