use crate::cmd::completions::ShellType;
use anyhow::Result;
use clap::{Parser, Subcommand};
use std::io;
use std::path::PathBuf;
use std::process::ExitCode;
mod context;
mod directories;
mod display_context;
mod generate_synthetic;
mod lex;
mod linked;
mod missing_open;
mod options;
mod parse;
mod region;
mod roundtrip;
mod stats;
#[derive(Parser, Debug)]
#[command(name = "bean-doctor")]
#[command(author, version, about, long_about = None)]
pub struct Args {
#[arg(long, value_name = "SHELL", hide = true)]
generate_completions: Option<ShellType>,
#[command(subcommand)]
pub command: Option<Command>,
}
#[derive(Subcommand, Debug)]
pub enum Command {
#[command(alias = "dump-lexer")]
Lex {
file: PathBuf,
},
Parse {
file: PathBuf,
#[arg(short, long)]
verbose: bool,
},
Context {
file: PathBuf,
line: usize,
},
Linked {
file: PathBuf,
location: String,
},
MissingOpen {
file: PathBuf,
},
ListOptions,
PrintOptions {
file: PathBuf,
},
Stats {
file: PathBuf,
},
DisplayContext {
file: PathBuf,
},
Roundtrip {
file: PathBuf,
},
Directories {
file: PathBuf,
#[arg(value_name = "DIR")]
dirs: Vec<PathBuf>,
},
Region {
file: PathBuf,
start_line: usize,
end_line: usize,
#[arg(long, value_enum)]
conversion: Option<Conversion>,
},
GenerateSynthetic {
#[arg(short, long, default_value = "tests/compatibility/synthetic")]
output: PathBuf,
#[arg(short, long, default_value = "50")]
count: usize,
#[arg(short, long)]
seed: Option<u64>,
#[arg(long)]
skip_validation: bool,
#[arg(long)]
manifest: bool,
#[arg(long)]
edge_cases_only: bool,
},
}
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
pub enum Conversion {
Value,
Cost,
}
pub fn main_with_name(bin_name: &str) -> ExitCode {
let args = Args::parse();
if let Some(shell) = args.generate_completions {
crate::cmd::completions::generate_completions::<Args>(shell, bin_name);
return ExitCode::SUCCESS;
}
let Some(command) = args.command else {
eprintln!("error: a subcommand is required");
eprintln!("For more information, try '--help'");
return ExitCode::from(2);
};
match run(command) {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("error: {e:#}");
ExitCode::from(1)
}
}
}
pub fn run(command: Command) -> Result<()> {
let mut stdout = io::stdout().lock();
match command {
Command::Lex { file } => lex::cmd_lex(&file, &mut stdout),
Command::Parse { file, verbose } => parse::cmd_parse(&file, verbose, &mut stdout),
Command::Context { file, line } => context::cmd_context(&file, line, &mut stdout),
Command::Linked { file, location } => linked::cmd_linked(&file, &location, &mut stdout),
Command::MissingOpen { file } => missing_open::cmd_missing_open(&file, &mut stdout),
Command::ListOptions => options::cmd_list_options(&mut stdout),
Command::PrintOptions { file } => options::cmd_print_options(&file, &mut stdout),
Command::Stats { file } => stats::cmd_stats(&file, &mut stdout),
Command::DisplayContext { file } => {
display_context::cmd_display_context(&file, &mut stdout)
}
Command::Roundtrip { file } => roundtrip::cmd_roundtrip(&file, &mut stdout),
Command::Directories { file, dirs } => {
directories::cmd_directories(&file, &dirs, &mut stdout)
}
Command::Region {
file,
start_line,
end_line,
conversion,
} => region::cmd_region(&file, start_line, end_line, conversion, &mut stdout),
Command::GenerateSynthetic {
output,
count,
seed,
skip_validation,
manifest,
edge_cases_only,
} => generate_synthetic::cmd_generate_synthetic(
&output,
count,
seed,
skip_validation,
manifest,
edge_cases_only,
&mut stdout,
),
}
}