use std::{collections::HashMap, error::Error, process::Command, str::FromStr};
use clap::Args;
use spinners::{Spinner, Spinners};
use strum::IntoEnumIterator;
use crate::{
config,
templates::{templater::Templater, CLITemplate, Renderer},
};
#[derive(Args, Debug)]
pub struct InitOptions {
#[arg(long, value_name = "TITLE")]
pub title: Option<String>,
#[arg(long, value_delimiter = ',', value_name = "TAG1,TAG2,...")]
pub tags: Option<Vec<String>>,
#[arg(long, value_name = "COLOR")]
pub color: Option<String>,
#[arg(long, value_name = "FORMAT")]
pub template: Option<String>,
#[arg(long, value_name = "SPDX_ID")]
pub license: Option<String>,
#[arg(long, value_name = "TEXT")]
pub description: Option<String>,
}
pub fn init(opts: InitOptions) -> Result<(), Box<dyn Error>> {
let template = match opts.template {
Some(ref arg_template) => {
CLITemplate::from_str(arg_template).map_err(|_| "Invalid template".to_string())?
}
None => prompt_template()?,
};
let conf = config::create_conf(opts)?;
std::fs::create_dir_all(&conf.title)?;
let output = Command::new("npm")
.args(["init", "-y", "--prefix", &conf.title])
.current_dir(&conf.title)
.output()?;
if !output.status.success() {
eprintln!(
"npm init failed: {}",
String::from_utf8_lossy(&output.stderr)
);
return Err("npm init failed".into());
}
let npm_commands = vec![
format!("install --save-dev vite@5.4.21 http-server@14.1.1"),
format!("install reveal.js@5.2.1 serve@14.2.6 highlight.js@11.12.0"),
format!("install patch-package@8.0.1"),
];
for c in npm_commands {
let mut spinner = Spinner::new(Spinners::Dots9, "Initializing...".into());
let args: Vec<&str> = c.split(' ').collect();
let output = Command::new("npm")
.args(&args)
.current_dir(&conf.title)
.output()?;
if !output.status.success() {
eprintln!(
"npm {} failed: {}",
c,
String::from_utf8_lossy(&output.stderr)
);
return Err(format!("npm {} failed", c).into());
}
spinner.stop();
println!("Bootstrapped npm {}", c);
}
println!("Saving config file...");
config::write_config(&conf.title, &conf)?;
let mut params: HashMap<String, String> = HashMap::new();
params.insert(String::from("TITLE"), conf.title.clone());
let templater = Templater::new(template, params);
templater.write_to_fs(&conf.title)?;
Ok(())
}
fn prompt_template() -> Result<CLITemplate, Box<dyn Error>> {
let template_opts: Vec<CLITemplate> = CLITemplate::iter().collect();
let chosen_template = inquire::Select::new("Select a template:", template_opts).prompt()?;
Ok(chosen_template)
}