Feat: improve CLI with help menu and multi-file support

Extracts all argument-parsing and help logic into cli.rs:
- -h / --help: colored usage and options summary
- -V / --version: print version from Cargo.toml
- Unknown flags produce a styled error with a hint
- At least one file required; missing-file error is styled
- Multiple files accepted; exit code 1 if any file has parse errors
This commit is contained in:
2026-03-10 18:49:15 +01:00
parent 5bf4a494cb
commit 415dea6fd2
2 changed files with 105 additions and 11 deletions

87
fluxc/src/cli.rs Normal file
View File

@@ -0,0 +1,87 @@
use std::process;
use yansi::Paint as _;
// ── Help ───────────────────────────────────────────────────────────────────────
pub fn print_help() {
println!(
"{} {} — the Flux language compiler\n",
"fluxc".bold(),
env!("CARGO_PKG_VERSION").dim(),
);
println!("{}", "USAGE:".bold().yellow());
println!(" fluxc [OPTIONS] <file> [<file> ...]\n");
println!("{}", "OPTIONS:".bold().yellow());
println!(
" {}, {} Print this help message",
"-h".bold(),
"--help".bold(),
);
println!(
" {}, {} Print version information",
"-V".bold(),
"--version".bold(),
);
println!();
println!("{}", "ARGS:".bold().yellow());
println!(
" {} One or more Flux source files to compile",
"<file>".bold(),
);
}
pub fn print_version() {
println!("fluxc {}", env!("CARGO_PKG_VERSION"));
}
// ── Error helpers ─────────────────────────────────────────────────────────────
pub fn fatal(msg: &str) -> ! {
eprintln!("{}: {}", "error".bold().red(), msg.bold());
eprintln!(" {} fluxc --help", "hint:".bold().cyan());
process::exit(1);
}
pub fn io_error(path: &str, err: std::io::Error) -> ! {
eprintln!(
"{}: cannot read {}: {}",
"error".bold().red(),
path.bold(),
err,
);
process::exit(1);
}
// ── Argument parsing ──────────────────────────────────────────────────────────
pub struct Opts {
pub files: Vec<String>,
}
pub fn parse_args() -> Opts {
let mut files = Vec::new();
for arg in std::env::args().skip(1) {
match arg.as_str() {
"-h" | "--help" => {
print_help();
process::exit(0);
}
"-V" | "--version" => {
print_version();
process::exit(0);
}
flag if flag.starts_with('-') => {
fatal(&format!("unknown option `{flag}`"));
}
path => files.push(path.to_owned()),
}
}
if files.is_empty() {
fatal("no input files — at least one source file is required");
}
Opts { files }
}

View File

@@ -1,26 +1,33 @@
use std::{env::args, fs}; use std::{fs, process};
use crate::parser::Parser; use crate::parser::Parser;
pub mod ast; pub mod ast;
pub mod cli;
pub mod diagnostics; pub mod diagnostics;
pub mod lexer; pub mod lexer;
pub mod parser; pub mod parser;
pub mod token; pub mod token;
fn main() { fn main() {
let path = args().nth(1).expect("usage: fluxc <file>"); let opts = cli::parse_args();
let content = fs::read_to_string(&path).unwrap_or_else(|e| { let mut had_errors = false;
eprintln!("error: {e}");
std::process::exit(1) for path in &opts.files {
}); let content = fs::read_to_string(path).unwrap_or_else(|e| cli::io_error(path, e));
let mut parser = Parser::new(&content); let mut parser = Parser::new(&content);
let program = parser.parse_program(); let program = parser.parse_program();
for diag in &parser.errors { for diag in &parser.errors {
eprint!("{}", diag.render(&content, &path)); eprint!("{}", diag.render(&content, path));
had_errors = true;
} }
println!("{program:#?}"); println!("{program:#?}");
} }
if had_errors {
process::exit(1);
}
}