mirror of
https://github.com/encounter/objdiff.git
synced 2026-03-30 11:32:16 -07:00
cad9b70632
This migrates to using protobuf to define the "report" and "changes" formats in objdiff-cli. The JSON output now uses the Proto3 "JSON Mapping", which is slightly incompatible with the existing JSON format. Mainly, 64-bit numbers are represented as strings, and addresses are decimal strings instead of hex. However, the older JSON format is still accepted by "report changes" to ease migration.
142 lines
4.1 KiB
Rust
142 lines
4.1 KiB
Rust
mod argp_version;
|
|
mod cmd;
|
|
mod util;
|
|
|
|
use std::{env, ffi::OsStr, fmt::Display, path::PathBuf, str::FromStr};
|
|
|
|
use anyhow::{Error, Result};
|
|
use argp::{FromArgValue, FromArgs};
|
|
use enable_ansi_support::enable_ansi_support;
|
|
use supports_color::Stream;
|
|
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
|
|
|
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
|
enum LogLevel {
|
|
Error,
|
|
Warn,
|
|
Info,
|
|
Debug,
|
|
Trace,
|
|
}
|
|
|
|
impl FromStr for LogLevel {
|
|
type Err = ();
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(match s {
|
|
"error" => Self::Error,
|
|
"warn" => Self::Warn,
|
|
"info" => Self::Info,
|
|
"debug" => Self::Debug,
|
|
"trace" => Self::Trace,
|
|
_ => return Err(()),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Display for LogLevel {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str(match self {
|
|
LogLevel::Error => "error",
|
|
LogLevel::Warn => "warn",
|
|
LogLevel::Info => "info",
|
|
LogLevel::Debug => "debug",
|
|
LogLevel::Trace => "trace",
|
|
})
|
|
}
|
|
}
|
|
|
|
impl FromArgValue for LogLevel {
|
|
fn from_arg_value(value: &OsStr) -> Result<Self, String> {
|
|
String::from_arg_value(value)
|
|
.and_then(|s| Self::from_str(&s).map_err(|_| "Invalid log level".to_string()))
|
|
}
|
|
}
|
|
|
|
#[derive(FromArgs, PartialEq, Debug)]
|
|
/// Yet another GameCube/Wii decompilation toolkit.
|
|
struct TopLevel {
|
|
#[argp(subcommand)]
|
|
command: SubCommand,
|
|
#[argp(option, short = 'C')]
|
|
/// Change working directory.
|
|
chdir: Option<PathBuf>,
|
|
#[argp(option, short = 'L')]
|
|
/// Minimum logging level. (Default: info)
|
|
/// Possible values: error, warn, info, debug, trace
|
|
log_level: Option<LogLevel>,
|
|
/// Print version information and exit.
|
|
#[argp(switch, short = 'V')]
|
|
version: bool,
|
|
/// Disable color output. (env: NO_COLOR)
|
|
#[argp(switch)]
|
|
no_color: bool,
|
|
}
|
|
|
|
#[derive(FromArgs, PartialEq, Debug)]
|
|
#[argp(subcommand)]
|
|
enum SubCommand {
|
|
Diff(cmd::diff::Args),
|
|
Report(cmd::report::Args),
|
|
}
|
|
|
|
// Duplicated from supports-color so we can check early.
|
|
fn env_no_color() -> bool {
|
|
match env::var("NO_COLOR").as_deref() {
|
|
Ok("") | Ok("0") | Err(_) => false,
|
|
Ok(_) => true,
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let args: TopLevel = argp_version::from_env();
|
|
let use_colors = if args.no_color || env_no_color() {
|
|
false
|
|
} else {
|
|
// Try to enable ANSI support on Windows.
|
|
let _ = enable_ansi_support();
|
|
// Disable isatty check for supports-color. (e.g. when used with ninja)
|
|
unsafe { env::set_var("IGNORE_IS_TERMINAL", "1") };
|
|
supports_color::on(Stream::Stdout).is_some_and(|c| c.has_basic)
|
|
};
|
|
|
|
let format =
|
|
tracing_subscriber::fmt::format().with_ansi(use_colors).with_target(false).without_time();
|
|
let builder = tracing_subscriber::fmt().event_format(format).with_writer(std::io::stderr);
|
|
if let Some(level) = args.log_level {
|
|
builder
|
|
.with_max_level(match level {
|
|
LogLevel::Error => LevelFilter::ERROR,
|
|
LogLevel::Warn => LevelFilter::WARN,
|
|
LogLevel::Info => LevelFilter::INFO,
|
|
LogLevel::Debug => LevelFilter::DEBUG,
|
|
LogLevel::Trace => LevelFilter::TRACE,
|
|
})
|
|
.init();
|
|
} else {
|
|
builder
|
|
.with_env_filter(
|
|
EnvFilter::builder()
|
|
.with_default_directive(LevelFilter::INFO.into())
|
|
.from_env_lossy(),
|
|
)
|
|
.init();
|
|
}
|
|
|
|
let mut result = Ok(());
|
|
if let Some(dir) = &args.chdir {
|
|
result = env::set_current_dir(dir).map_err(|e| {
|
|
Error::new(e)
|
|
.context(format!("Failed to change working directory to '{}'", dir.display()))
|
|
});
|
|
}
|
|
result = result.and_then(|_| match args.command {
|
|
SubCommand::Diff(c_args) => cmd::diff::run(c_args),
|
|
SubCommand::Report(c_args) => cmd::report::run(c_args),
|
|
});
|
|
if let Err(e) = result {
|
|
eprintln!("Failed: {e:?}");
|
|
std::process::exit(1);
|
|
}
|
|
}
|