use clap::{Arg, Command};
use std::fs;
use std::time::Instant;
use std::path::PathBuf;
use ilm_core::{parser::Parser, interpreter::Interpreter, highlighter};
fn main() {
println!("\x1B[32m> ilm\x1B[0m");
let matches = define_cli().get_matches();
if matches.get_flag("version") {
println!("{}", env!("CARGO_PKG_VERSION"));
std::process::exit(0);
}
if let Some(enable_value) = matches.get_one::<String>("enable") {
handle_enable(enable_value, matches.get_flag("force"));
} else if let Some(sub_matches) = matches.subcommand_matches("run") {
run_ilm_file(sub_matches);
}
}
fn define_cli() -> Command {
Command::new("milaf")
.about("A command-line tool for running ilm programs")
.arg_required_else_help(true)
.arg(
Arg::new("enable")
.short('e')
.long("enable")
.value_name("FEATURE")
.help("Enable a feature (e.g., 'language' for VS Code support)")
.num_args(1),
).arg(
Arg::new("force")
.short('f')
.long("force")
.help("Force enablement of the feature, overwriting existing configurations")
.action(clap::ArgAction::SetTrue)
.requires("enable"), )
.arg(
Arg::new("version")
.short('v')
.long("version")
.help("Print version information and exit")
.action(clap::ArgAction::SetTrue),
)
.subcommand(
Command::new("run")
.about("Run an ilm program")
.arg(
Arg::new("file")
.help("The .ilm file to run")
.required(true)
.index(1),
),
)
}
fn run_ilm_file(matches: &clap::ArgMatches) {
let file_path = matches.get_one::<String>("file").unwrap();
let start_time = Instant::now();
if !is_extension_installed() {
println!("Warning: ilm VS Code extension not detected. Run 'milaf -enable language' to install it.");
}
let code = fs::read_to_string(file_path)
.unwrap_or_else(|_| panic!("Failed to read file: {}", file_path));
let tokens = ilm_core::lexer::Lexer::new(&code).tokenize();
let mut parser = Parser::new(tokens, &code);
match parser.parse() {
Ok(ast) => {
let mut interp = Interpreter::new();
if let Err(e) = interp.run(ast) {
println!("{}", e.to_string());
} else {
let duration = start_time.elapsed();
println!(
"\n\x1B[94m+---------------------------------+\n| Masha'Allah! Aiktamal altanfidh |\n+---------------------------------+\x1B[0m\n\x1B[35m{:?}\x1B[0m",
duration
);
}
}
Err(e) => println!("{}", e.to_string()),
}
}
fn handle_enable(feature: &str, force: bool) {
match feature {
"language" => {
let ext_dir = get_extension_dir();
if ext_dir.exists() && !force {
println!("ilm extension already installed at {:?}. Use 'milaf -e language --force' to reinstall.", ext_dir);
} else {
if force && ext_dir.exists() {
std::fs::remove_dir_all(&ext_dir).unwrap_or_else(|e| eprintln!("Failed to remove existing extension: {}", e));
}
match highlighter::initialize_highlighting() {
Ok(()) => println!("ilm language support enabled successfully! Restart VS Code to apply."),
Err(e) => eprintln!("Failed to enable language support: {}", e),
}
}
}
other => eprintln!("Unknown feature '{}'. Supported: 'language'", other),
}
}
fn is_extension_installed() -> bool {
get_extension_dir().exists()
}
fn get_extension_dir() -> PathBuf {
let mut ext_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
ext_dir.push(".vscode");
ext_dir.push("extensions");
ext_dir.push(format!("besaoct.ilm-{}", env!("CARGO_PKG_VERSION")));
ext_dir
}