use std::io::{Write as _, stdout};
use std::path::Path;
use api::LibBuilder;
use clap::builder::Styles;
use clap::builder::styling::Style;
use clap::{Arg, Command, ValueHint, crate_authors, crate_description, crate_name, crate_version};
use color_eyre::Report;
use mlua::prelude::*;
use module::{Module, collect_modules_from_table};
pub mod api;
pub mod module;
const HELP_TEMPLATE: &str = "\
{name} v{version}
{tab}Authored by {author}
OVERVIEW: {name}
{tab}{about}
USAGE: {usage}
{positionals}
OPTIONS:
{options}
SUBCOMMANDS:
{subcommands}
";
const SUBCOMMAND_HELP_TEMPLATE: &str = "\
OVERVIEW: {name}
{tab}{about}
USAGE: {usage}
{positionals}
OPTIONS:
{options}
";
const SUBCOMMAND_WITH_SUBCOMMANDS_HELP_TEMPLATE: &str = "\
OVERVIEW: {name}
{tab}{about}
USAGE: {usage}
{positionals}
TARGETS:
{subcommands}
OPTIONS:
{options}
";
fn main() -> Result<(), Report> {
color_eyre::install()?;
let file_arg = Arg::new("file")
.short('f')
.long("file")
.default_value("build.lua")
.value_hint(ValueHint::FilePath)
.help("The path to the build.lua file to load build methods from.");
let mut command = Command::default().arg(file_arg);
let pre_args = command
.clone()
.ignore_errors(true)
.disable_help_flag(true)
.try_get_matches()
.ok()
.and_then(|a| a.get_one::<String>("file").cloned())
.unwrap_or_else(|| "build.lua".to_owned());
let lua = LibBuilder::init_lua().map_err(Report::new)?;
let table = {
let script = lua.load(Path::new(&pre_args));
script.call::<LuaTable>(())?
};
let modules = collect_modules_from_table(&table);
let exec = Command::default()
.name("exec")
.subcommands(modules.iter().map(Module::make_command))
.subcommand_required(true)
.disable_help_subcommand(true)
.arg_required_else_help(true)
.help_template(SUBCOMMAND_WITH_SUBCOMMANDS_HELP_TEMPLATE)
.about("Executes a command from the build.lua file.");
let list = Command::default()
.name("list")
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.num_args(0)
.help_heading("v"),
)
.help_template(SUBCOMMAND_HELP_TEMPLATE)
.about("Lists the detected commands from the build.lua file.");
command = command
.name(crate_name!())
.version(crate_version!())
.about(crate_description!())
.author(crate_authors!())
.help_template(HELP_TEMPLATE)
.styles(Styles::plain().placeholder(Style::new().dimmed()))
.subcommand_required(true)
.arg_required_else_help(true)
.allow_missing_positional(true)
.subcommand(exec)
.subcommand(list);
let args = command.get_matches();
match args.subcommand() {
Some(("exec", extras)) => match extras.subcommand() {
Some((subcommand, subcommand_extras)) => {
if let Some(find) = modules.into_iter().find(|named| named.name == subcommand) {
find.execute()?;
}
}
None => return Err(Report::msg("Supposedly unreachable branch executed")),
},
Some(("list", _extras)) => {
let mut out = stdout().lock();
writeln!(out, "COMMANDS:")?;
let longest_len = modules
.iter()
.map(|a| a.name.len())
.max()
.ok_or_else(|| Report::msg("{pre_args:?}"))?;
for module in modules {
if let Some(description) = module.description {
writeln!(
out,
" {:<width$} {description}",
module.name,
width = longest_len
)?;
} else {
writeln!(out, " {}", module.name)?;
}
}
out.flush()?;
drop(out);
}
None | Some(_) => {
return Err(Report::msg("Supposedly unreachable branch executed"));
}
}
Ok(())
}