use crate::commands::{init::init_script_file, script::run_script, Commands, script::Scripts, show::show_scripts, completions::generate_completions, validate::{validate_scripts, print_validation_results}};
use crate::error::CargoScriptError;
use std::fs;
use clap::{Parser, CommandFactory};
use colored::*;
#[derive(Parser, Debug)]
#[command(name = "cargo-script")]
pub struct Cli {
#[command(subcommand)]
command: Commands,
#[arg(long, default_value = "Scripts.toml", global = true)]
scripts_path: String,
}
pub fn run_with_error_handling() {
if let Err(e) = run() {
eprintln!("{}", e);
std::process::exit(1);
}
}
pub fn run() -> Result<(), CargoScriptError> {
let args: Vec<String> = std::env::args().collect();
let cli = if args.len() > 1 && args[1] == "script" {
let mut cargo_args = vec![args[0].clone()]; cargo_args.extend(args.into_iter().skip(2));
if cargo_args.len() == 1 {
let mut app = Cli::command();
app.print_help().unwrap();
std::process::exit(0);
}
if cargo_args.len() == 2 && (cargo_args[1] == "--help" || cargo_args[1] == "-h") {
let mut app = Cli::command();
app.print_help().unwrap();
std::process::exit(0);
}
Cli::try_parse_from(cargo_args).unwrap_or_else(|e| {
e.exit()
})
} else {
Cli::parse()
};
if !matches!(cli.command, Commands::Completions { .. } | Commands::Run { dry_run: true, .. }) {
let init_msg = format!("A CLI tool to run custom scripts in Rust, defined in [ Scripts.toml ] {}", emoji::objects::computer::FLOPPY_DISK.glyph);
print_framed_message(&init_msg);
}
let scripts_path = &cli.scripts_path;
match &cli.command {
Commands::Run { script, env, dry_run } => {
let scripts_content = fs::read_to_string(scripts_path)
.map_err(|e| CargoScriptError::ScriptFileNotFound {
path: scripts_path.clone(),
source: e,
})?;
let scripts: Scripts = toml::from_str(&scripts_content)
.map_err(|e| {
let message = e.message().to_string();
let line = e.span().map(|s| s.start);
CargoScriptError::InvalidToml {
path: scripts_path.clone(),
message,
line,
}
})?;
run_script(&scripts, script, env.clone(), *dry_run)?;
}
Commands::Init => {
init_script_file(Some(scripts_path));
}
Commands::Show => {
let scripts_content = fs::read_to_string(scripts_path)
.map_err(|e| CargoScriptError::ScriptFileNotFound {
path: scripts_path.clone(),
source: e,
})?;
let scripts: Scripts = toml::from_str(&scripts_content)
.map_err(|e| {
let message = e.message().to_string();
let line = e.span().map(|s| s.start);
CargoScriptError::InvalidToml {
path: scripts_path.clone(),
message,
line,
}
})?;
show_scripts(&scripts);
}
Commands::Completions { shell } => {
let mut app = Cli::command();
generate_completions(shell.clone(), &mut app);
}
Commands::Validate => {
let scripts_content = fs::read_to_string(scripts_path)
.map_err(|e| CargoScriptError::ScriptFileNotFound {
path: scripts_path.clone(),
source: e,
})?;
let scripts: Scripts = toml::from_str(&scripts_content)
.map_err(|e| {
let message = e.message().to_string();
let line = e.span().map(|s| s.start);
CargoScriptError::InvalidToml {
path: scripts_path.clone(),
message,
line,
}
})?;
let validation_result = validate_scripts(&scripts);
print_validation_results(&validation_result);
if !validation_result.is_valid() {
std::process::exit(1);
}
}
}
Ok(())
}
fn print_framed_message(message: &str) {
let framed_message = format!("| {} |", message);
let frame = "-".repeat(framed_message.len()-2);
println!("\n{}\n{}\n{}\n", frame.yellow(), framed_message.yellow(), frame.yellow());
}