use clap::{Parser, Subcommand};
use std::env;
use std::io::{self, Write};
use std::path::PathBuf;
fn print_top_usage_and_exit(program: &str, code: i32) -> ! {
eprintln!(
r#"Usage:
{0} read [--debug|-d] "<code>" # Run Brainfuck code (args are concatenated)
{0} read [--debug|-d] --file <PATH> # Run Brainfuck code loaded from file
{0} write [--bytes] [TEXT...] # Generate Brainfuck to print TEXT/STDIN/file
{0} write [--bytes] --file <PATH> # Generate Brainfuck to print file contents
{0} repl # Start a Brainfuck REPL (read-eval-print loop)
{0} ide [--file <PATH>] # Start a terminal-based Brainfuck IDE, optionally loading a file
Run "{0} <subcommand> --help" for more info.
"#,
program
);
let _ = io::stderr().flush();
std::process::exit(code);
}
#[derive(Parser, Debug)]
#[command(name = "bf", disable_help_flag = true, disable_help_subcommand = true)]
struct Cli {
#[arg(short = 'h', long = "help", action = clap::ArgAction::SetTrue)]
help: bool,
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Subcommand, Debug)]
enum Command {
Read(rust_bf::commands::read::ReadArgs),
Write(rust_bf::commands::write::WriteArgs),
Repl(rust_bf::commands::repl::ReplArgs),
Ide(rust_bf::commands::ide::IdeArgs),
}
fn main() {
let program = env::args().next().unwrap_or_else(|| String::from("bf"));
let cli = Cli::parse();
if cli.help {
print_top_usage_and_exit(&program, 0);
}
let code = match cli.command {
Some(Command::Read(args)) => rust_bf::commands::read::run(&program, args),
Some(Command::Write(args)) => rust_bf::commands::write::run(&program, args),
Some(Command::Repl(args)) => {
let program = "repl";
let mode_flag = if args.bare {
rust_bf::repl::ModeFlagOverride::Bare
} else if args.editor {
rust_bf::repl::ModeFlagOverride::Editor
} else {
rust_bf::repl::ModeFlagOverride::None
};
let code = rust_bf::commands::repl::run(&program, false, mode_flag);
std::process::exit(code);
},
Some(Command::Ide(args)) => {
let filename = if let Some(filename) = &args.filename {
if args.help {
eprintln!("Error: --help cannot be used with --file");
std::process::exit(1);
}
Some(PathBuf::from(filename))
} else {
None
};
rust_bf::commands::ide::run(&program, args.help, filename, args.vi_mode)
}
None => {
rust_bf::commands::repl::run(&program, false, rust_bf::repl::ModeFlagOverride::None)
}
};
std::process::exit(code);
}