use bugstalker::debugger::rust;
use bugstalker::log::LOGGER_SWITCHER;
use bugstalker::ui;
use bugstalker::ui::config::{Theme, UIConfig};
use bugstalker::ui::supervisor::{DebugeeSource, Interface};
use clap::error::ErrorKind;
use clap::{CommandFactory, Parser};
use std::fmt::Display;
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
pub struct Args {
#[clap(long)]
#[arg(default_value_t = false)]
tui: bool,
#[clap(long)]
#[arg(default_value_t = false)]
dap: bool,
#[clap(long, short)]
pid: Option<i32>,
#[clap(long)]
cwd: Option<PathBuf>,
debugee: Option<String>,
#[clap(short, long)]
std_lib_path: Option<String>,
#[clap(short, long)]
oracle: Vec<String>,
#[arg(raw(true))]
args: Vec<String>,
#[clap(short, long)]
#[arg(default_value = "solarized_dark")]
theme: String,
#[clap(long, env)]
keymap_file: Option<String>,
#[clap(long, env)]
#[arg(default_value_t = false)]
save_history: bool,
}
fn print_fatal_and_exit(kind: ErrorKind, message: impl Display) -> ! {
let mut cmd = Args::command();
_ = cmd.error(kind, message).print();
exit(1);
}
trait FatalResult<T> {
fn unwrap_or_exit(self, kind: ErrorKind, message: impl Display) -> T;
}
impl<T, E: Display> FatalResult<T> for Result<T, E> {
fn unwrap_or_exit(self, kind: ErrorKind, message: impl Display) -> T {
match self {
Ok(ok) => ok,
Err(err) => print_fatal_and_exit(kind, format!("{message}: {err:#}")),
}
}
}
impl From<&Args> for UIConfig {
fn from(args: &Args) -> Self {
Self {
theme: Theme::from_str(&args.theme)
.unwrap_or_exit(ErrorKind::InvalidValue, "Not an available theme"),
tui_keymap: ui::tui::config::KeyMap::from_file(args.keymap_file.as_deref())
.unwrap_or_default(),
save_history: args.save_history,
}
}
}
fn main() {
let logger = env_logger::Logger::from_default_env();
let filter = logger.filter();
LOGGER_SWITCHER.switch(logger, filter);
let args = Args::parse();
ui::config::set(UIConfig::from(&args));
rust::Environment::init(args.std_lib_path.map(PathBuf::from));
let debugee_src = || {
if let Some(ref debugee) = args.debugee {
DebugeeSource::File {
path: debugee,
args: &args.args,
cwd: args.cwd.as_deref(),
}
} else if let Some(pid) = args.pid {
DebugeeSource::Process { pid }
} else {
print_fatal_and_exit(
ErrorKind::ArgumentConflict,
"Please provide a debugee name or use a \"-p\" option for attach to already running process",
);
}
};
let interface = if args.tui {
Interface::TUI {
source: debugee_src(),
}
} else if args.dap {
Interface::DAP
} else {
Interface::Default {
source: debugee_src(),
}
};
ui::supervisor::Supervisor::run(interface, &args.oracle)
.unwrap_or_exit(ErrorKind::InvalidSubcommand, "Application error")
}