use std::path::PathBuf;
use anyhow::anyhow;
use cargo_generate::{GenerateArgs as CargoGenerateArgs, TemplatePath};
use clap::Parser;
use dialoguer::Input;
use thiserror::Error;
use crate::cli::commands::template::init_metadata;
use crate::{
cli::{command::project_name_parser, config::Config, util},
git::repository::GitRepository,
loading,
templates::Collector,
};
#[derive(Clone, Parser, Debug)]
pub struct CreateArgs {
#[arg(value_parser = project_name_parser)]
pub name: Option<String>,
#[arg(short = 't', long)]
pub template: Option<String>,
#[arg(long, short = 'o', value_name = "PATH", default_value = crate::cli::command::default_output_dir().into_os_string())]
pub output: PathBuf,
#[arg(long, default_value_t = false)]
pub skip_init: bool,
#[arg(long, default_value_t = false)]
pub skip_metadata: bool,
#[arg(long, short = 'v')]
pub verbose: bool,
}
#[derive(Error, Debug)]
pub enum CreateHandlerError {
#[error("Template not found by name: {0}. Possible values: {1:?}")]
TemplateNotFound(String, Vec<String>),
}
pub async fn handle(config: Config, template_repo_dir: PathBuf, mut args: CreateArgs) -> anyhow::Result<()> {
let name = match args.name.take() {
Some(name) => name,
None => {
let raw: String = Input::new().with_prompt("Template crate name").interact_text()?;
project_name_parser(&raw).map_err(|e| anyhow!(e))?
},
};
let templates = loading!(
"Collecting available templates",
Collector::new(template_repo_dir.join(&config.template_repository.folder))
.collect()
.await
)?;
let template = match &args.template {
Some(template_id) => templates
.iter()
.rfind(|t| t.id().eq_ignore_ascii_case(template_id) || t.name().eq_ignore_ascii_case(template_id))
.ok_or_else(|| {
CreateHandlerError::TemplateNotFound(
template_id.to_string(),
templates.iter().map(|t| t.id().to_string()).collect(),
)
})?,
None => util::cli_select("đ Select a template", templates.as_slice())?,
};
let template_path = template
.path()
.to_str()
.ok_or(anyhow!("Invalid template path!"))?
.to_string();
let generate_args = CargoGenerateArgs {
name: Some(name.clone()),
destination: Some(args.output.clone()),
template_path: TemplatePath {
path: Some(template_path),
..TemplatePath::default()
},
verbose: args.verbose,
..CargoGenerateArgs::default()
};
loading!("Generating template crate", cargo_generate::generate(generate_args))?;
let crate_dir = args.output.join(&name);
if !args.skip_metadata {
loading!(
format!("Initialising template metadata for **{}**", name),
init_metadata::auto_init(&crate_dir).await
)?;
}
if !args.skip_init
&& let Err(error) = GitRepository::new(crate_dir).init()
&& args.verbose
{
println!("âšī¸ Git repository already initialized: {error}");
}
Ok(())
}