use crate::actions::GlobalArgs;
use crate::actions::text_manipulation::{input_prompt_for, select_prompt_for};
use crate::render::json::JsonToStdout;
use crate::render::ui::{fuzzy_select_with_key, multi_fuzzy_select_with_key};
use crate::types::api::privacy_type::Privacy;
use crate::types::api::service_type::Service;
use crate::types::context::BergContext;
use clap::Parser;
use forgejo_api::structs::{MigrateRepoOptions, MigrateRepoOptionsService};
use miette::IntoDiagnostic;
use strum::{Display, VariantArray};
#[derive(Parser, Debug)]
pub struct RepoMigrateArgs {
#[arg(short, long)]
pub clone_addr: Option<String>,
#[arg(long)]
pub description: Option<String>,
#[arg(short, long, value_enum, value_name = "VISIBILITY")]
pub private: Option<Privacy>,
#[arg(id = "repo-name", short, long)]
pub repo_name: Option<String>,
#[arg(short, long, value_enum, value_name = "SERVICE")]
pub service: Option<Service>,
}
#[derive(Display, PartialEq, Eq, VariantArray)]
enum CreatableFields {
Service,
Description,
Private,
}
impl RepoMigrateArgs {
pub async fn run(self, global_args: GlobalArgs) -> miette::Result<()> {
let ctx = BergContext::new(self, global_args).await?;
let options = create_options(&ctx).await?;
let repo = ctx.client.repo_migrate(options).await.into_diagnostic()?;
match ctx.global_args.output_mode {
crate::types::output::OutputMode::Pretty => {
tracing::debug!("{repo:?}");
}
crate::types::output::OutputMode::Json => {
repo.print_json()?;
}
}
Ok(())
}
}
async fn create_options(ctx: &BergContext<RepoMigrateArgs>) -> miette::Result<MigrateRepoOptions> {
let repo_name = match ctx.args.repo_name.as_ref() {
Some(name) => name.clone(),
None => {
if ctx.global_args.non_interactive {
miette::bail!(
"You need to provide a target repository name address in non-interactive mode!"
);
}
inquire::Text::new(input_prompt_for("New Repository Name").as_str())
.prompt()
.into_diagnostic()?
}
};
let clone_addr = match ctx.args.clone_addr.as_ref() {
Some(name) => name.clone(),
None => {
if ctx.global_args.non_interactive {
miette::bail!("You need to provide a clone address in non-interactive mode!");
}
inquire::Text::new(input_prompt_for("Repository clone address").as_str())
.prompt()
.into_diagnostic()?
}
};
let mut options = MigrateRepoOptions {
auth_password: None,
auth_token: None,
auth_username: None,
clone_addr,
description: ctx.args.description.clone(),
issues: None,
labels: None,
lfs: None,
lfs_endpoint: None,
milestones: None,
mirror: None,
mirror_interval: None,
pull_requests: None,
releases: None,
repo_name,
repo_owner: None,
service: ctx.args.service.map(|service| service.into()),
uid: None,
wiki: None,
private: ctx.args.private.map(|privacy| match privacy {
Privacy::Private => true,
Privacy::Public => false,
}),
};
if !ctx.global_args.non_interactive {
let optional_data = {
use CreatableFields::*;
[
(Service, ctx.args.service.is_none()),
(Description, ctx.args.description.is_none()),
(Private, ctx.args.private.is_none()),
]
.into_iter()
.filter_map(|(name, missing)| missing.then_some(name))
.collect::<Vec<_>>()
};
if !optional_data.is_empty() {
let chosen_optionals = multi_fuzzy_select_with_key(
&optional_data,
"Choose optional properties",
|_| false,
|o| o.to_string(),
)?;
{
use CreatableFields::*;
options.service =
migrate_service(ctx, chosen_optionals.contains(&&Service)).await?;
options.private = repo_private(ctx, chosen_optionals.contains(&&Private)).await?;
options.description =
repo_description(ctx, chosen_optionals.contains(&&Description)).await?;
}
}
}
Ok(options)
}
async fn migrate_service(
ctx: &BergContext<RepoMigrateArgs>,
interactive: bool,
) -> miette::Result<Option<MigrateRepoOptionsService>> {
let service = match ctx.args.service {
Some(service) => service.to_string(),
None => {
use Service::*;
if !interactive {
return Ok(None);
}
fuzzy_select_with_key(
&[
Git, Github, Gitea, Gitlab, Gogs, Onedev, Gitbucket, Codebase,
],
select_prompt_for("service"),
|service| service.to_string(),
)
.copied()?
.to_string()
}
};
let migraterepooptionsservice = serde_json::from_str(&service).unwrap();
Ok(Some(migraterepooptionsservice))
}
async fn repo_private(
ctx: &BergContext<RepoMigrateArgs>,
interactive: bool,
) -> miette::Result<Option<bool>> {
let privacy = match ctx.args.private {
Some(privacy) => match privacy {
Privacy::Private => true,
Privacy::Public => false,
},
None => {
if !interactive {
return Ok(None);
}
fuzzy_select_with_key(
&[true, false],
select_prompt_for("repo privacy"),
|private| {
if *private {
String::from("Private")
} else {
String::from("Public")
}
},
)
.copied()?
}
};
Ok(Some(privacy))
}
async fn repo_description(
ctx: &BergContext<RepoMigrateArgs>,
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 Repository description")?
}
};
Ok(Some(description))
}