use std::sync::{
atomic::{AtomicBool, Ordering},
OnceLock,
};
pub mod args;
pub mod command;
pub mod error;
pub mod input;
pub mod loader;
pub mod output;
pub mod parser;
pub mod shell;
pub mod validate;
pub use crate::command::Command as CliCustom;
use crate::loader::CommandRegistry;
#[cfg(feature = "internal-commands")]
pub mod commands;
#[cfg(feature = "custom-commands")]
pub mod custom;
pub struct ModCli {
pub registry: CommandRegistry,
}
pub fn set_startup_banner_from_file(path: &str) -> Result<(), crate::error::ModCliError> {
let data = std::fs::read_to_string(path)?;
let owned = data.clone();
set_startup_banner(move || {
println!("{owned}\n");
});
Ok(())
}
#[macro_export]
macro_rules! banner_text {
($text:expr) => {{
$crate::set_startup_banner(|| {
$crate::output::print::line($text);
println!();
});
}};
}
#[macro_export]
macro_rules! banner_file {
($path:expr) => {{
let _ = $crate::set_startup_banner_from_file($path);
}};
}
#[macro_export]
macro_rules! banner {
($body:block) => {{
$crate::set_startup_banner(|| $body);
}};
}
impl Default for ModCli {
fn default() -> Self {
Self::new()
}
}
impl ModCli {
pub fn new() -> Self {
Self {
registry: CommandRegistry::new(),
}
}
pub fn set_prefix(&mut self, prefix: &str) {
self.registry.set_prefix(prefix);
}
pub fn get_prefix(&self) -> &str {
self.registry.get_prefix()
}
pub fn run(&mut self, args: Vec<String>) {
run_startup_banner_if_enabled();
if args.is_empty() {
crate::output::hook::status("No command provided. Try `help`.");
return;
}
let command = &args[0];
let rest = &args[1..];
self.registry.execute(command, rest);
}
}
pub fn modcli_version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
static STARTUP_BANNER: OnceLock<Box<dyn Fn() + Send + Sync>> = OnceLock::new();
static BANNER_RAN: AtomicBool = AtomicBool::new(false);
pub fn set_startup_banner<F>(f: F)
where
F: Fn() + Send + Sync + 'static,
{
let _ = STARTUP_BANNER.set(Box::new(f));
}
fn run_startup_banner_if_enabled() {
if BANNER_RAN.swap(true, Ordering::SeqCst) {
return;
}
if let Ok(val) = std::env::var("MODCLI_DISABLE_BANNER") {
if val == "1" || val.eq_ignore_ascii_case("true") {
return;
}
}
if let Some(cb) = STARTUP_BANNER.get() {
cb();
}
}