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:
87
fluxc/src/cli.rs
Normal file
87
fluxc/src/cli.rs
Normal 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 }
|
||||
}
|
||||
@@ -1,26 +1,33 @@
|
||||
use std::{env::args, fs};
|
||||
use std::{fs, process};
|
||||
|
||||
use crate::parser::Parser;
|
||||
|
||||
pub mod ast;
|
||||
pub mod cli;
|
||||
pub mod diagnostics;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod token;
|
||||
|
||||
fn main() {
|
||||
let path = args().nth(1).expect("usage: fluxc <file>");
|
||||
let content = fs::read_to_string(&path).unwrap_or_else(|e| {
|
||||
eprintln!("error: {e}");
|
||||
std::process::exit(1)
|
||||
});
|
||||
let opts = cli::parse_args();
|
||||
let mut had_errors = false;
|
||||
|
||||
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 program = parser.parse_program();
|
||||
|
||||
for diag in &parser.errors {
|
||||
eprint!("{}", diag.render(&content, &path));
|
||||
eprint!("{}", diag.render(&content, path));
|
||||
had_errors = true;
|
||||
}
|
||||
|
||||
println!("{program:#?}");
|
||||
}
|
||||
|
||||
if had_errors {
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user