mirror of
https://github.com/encounter/objdiff.git
synced 2025-08-18 09:51:35 +00:00
Add CLI args to objdiff-gui (incl. --project-dir/-p)
Resolves #41 Resolves #211
This commit is contained in:
parent
247d6da94b
commit
b21892be31
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3517,6 +3517,7 @@ name = "objdiff-gui"
|
|||||||
version = "3.0.0-beta.14"
|
version = "3.0.0-beta.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"argp",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"const_format",
|
"const_format",
|
||||||
"cwdemangle",
|
"cwdemangle",
|
||||||
|
@ -25,6 +25,7 @@ wsl = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
argp = "0.4"
|
||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
const_format = "0.2"
|
const_format = "0.2"
|
||||||
cwdemangle = "1.0"
|
cwdemangle = "1.0"
|
||||||
|
@ -431,6 +431,7 @@ impl App {
|
|||||||
app_path: Option<PathBuf>,
|
app_path: Option<PathBuf>,
|
||||||
graphics_config: GraphicsConfig,
|
graphics_config: GraphicsConfig,
|
||||||
graphics_config_path: Option<PathBuf>,
|
graphics_config_path: Option<PathBuf>,
|
||||||
|
project_dir: Option<Utf8PlatformPathBuf>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Load previous app state (if any).
|
// Load previous app state (if any).
|
||||||
// Note that you must enable the `persistence` feature for this to work.
|
// Note that you must enable the `persistence` feature for this to work.
|
||||||
@ -440,18 +441,26 @@ impl App {
|
|||||||
app.appearance = appearance;
|
app.appearance = appearance;
|
||||||
}
|
}
|
||||||
if let Some(config) = deserialize_config(storage) {
|
if let Some(config) = deserialize_config(storage) {
|
||||||
let mut state = AppState { config, ..Default::default() };
|
let state = AppState { config, ..Default::default() };
|
||||||
if state.config.project_dir.is_some() {
|
|
||||||
state.config_change = true;
|
|
||||||
state.watcher_change = true;
|
|
||||||
}
|
|
||||||
if state.config.selected_obj.is_some() {
|
|
||||||
state.queue_build = true;
|
|
||||||
}
|
|
||||||
app.view_state.config_state.queue_check_update = state.config.auto_update_check;
|
|
||||||
app.state = Arc::new(RwLock::new(state));
|
app.state = Arc::new(RwLock::new(state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
let mut state = app.state.write().unwrap();
|
||||||
|
if let Some(project_dir) = project_dir
|
||||||
|
&& state.config.project_dir.as_ref().is_none_or(|p| *p != project_dir)
|
||||||
|
{
|
||||||
|
state.set_project_dir(project_dir);
|
||||||
|
}
|
||||||
|
if state.config.project_dir.is_some() {
|
||||||
|
state.config_change = true;
|
||||||
|
state.watcher_change = true;
|
||||||
|
}
|
||||||
|
if state.config.selected_obj.is_some() {
|
||||||
|
state.queue_build = true;
|
||||||
|
}
|
||||||
|
app.view_state.config_state.queue_check_update = state.config.auto_update_check;
|
||||||
|
}
|
||||||
app.appearance.init_fonts(&cc.egui_ctx);
|
app.appearance.init_fonts(&cc.egui_ctx);
|
||||||
app.appearance.utc_offset = utc_offset;
|
app.appearance.utc_offset = utc_offset;
|
||||||
app.app_path = app_path;
|
app.app_path = app_path;
|
||||||
|
63
objdiff-gui/src/argp_version.rs
Normal file
63
objdiff-gui/src/argp_version.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Originally from https://gist.github.com/suluke/e0c672492126be0a4f3b4f0e1115d77c
|
||||||
|
//! Extend `argp` to be better integrated with the `cargo` ecosystem
|
||||||
|
//!
|
||||||
|
//! For now, this only adds a --version/-V option which causes early-exit.
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
use argp::{EarlyExit, FromArgs, TopLevelCommand, parser::ParseGlobalOptions};
|
||||||
|
|
||||||
|
struct ArgsOrVersion<T>(T)
|
||||||
|
where T: FromArgs;
|
||||||
|
|
||||||
|
impl<T> TopLevelCommand for ArgsOrVersion<T> where T: FromArgs {}
|
||||||
|
|
||||||
|
impl<T> FromArgs for ArgsOrVersion<T>
|
||||||
|
where T: FromArgs
|
||||||
|
{
|
||||||
|
fn _from_args(
|
||||||
|
command_name: &[&str],
|
||||||
|
args: &[&OsStr],
|
||||||
|
parent: Option<&mut dyn ParseGlobalOptions>,
|
||||||
|
) -> Result<Self, EarlyExit> {
|
||||||
|
/// Also use argp for catching `--version`-only invocations
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
struct Version {
|
||||||
|
/// Print version information and exit.
|
||||||
|
#[argp(switch, short = 'V')]
|
||||||
|
pub version: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
match Version::from_args(command_name, args) {
|
||||||
|
Ok(v) => {
|
||||||
|
if v.version {
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
command_name.first().unwrap_or(&""),
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
);
|
||||||
|
std::process::exit(0);
|
||||||
|
} else {
|
||||||
|
// Pass through empty arguments
|
||||||
|
T::_from_args(command_name, args, parent).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(exit) => match exit {
|
||||||
|
EarlyExit::Help(_help) => {
|
||||||
|
// TODO: Chain help info from Version
|
||||||
|
// For now, we just put the switch on T as well
|
||||||
|
T::from_args(command_name, &["--help"]).map(Self)
|
||||||
|
}
|
||||||
|
EarlyExit::Err(_) => T::_from_args(command_name, args, parent).map(Self),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `FromArgs` type from the current process’s `env::args`.
|
||||||
|
///
|
||||||
|
/// This function will exit early from the current process if argument parsing was unsuccessful or if information like `--help` was requested.
|
||||||
|
/// Error messages will be printed to stderr, and `--help` output to stdout.
|
||||||
|
pub fn from_env<T>() -> T
|
||||||
|
where T: TopLevelCommand {
|
||||||
|
argp::parse_args_or_exit::<ArgsOrVersion<T>>(argp::DEFAULT).0
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod app_config;
|
mod app_config;
|
||||||
|
mod argp_version;
|
||||||
mod config;
|
mod config;
|
||||||
mod fonts;
|
mod fonts;
|
||||||
mod hotkeys;
|
mod hotkeys;
|
||||||
@ -11,19 +12,83 @@ mod update;
|
|||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
fmt::Display,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::ExitCode,
|
process::ExitCode,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
str::FromStr,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Result, ensure};
|
use anyhow::{Result, ensure};
|
||||||
|
use argp::{FromArgValue, FromArgs};
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
use objdiff_core::config::path::check_path_buf;
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::{EnvFilter, filter::LevelFilter};
|
||||||
|
use typed_path::Utf8PlatformPathBuf;
|
||||||
|
|
||||||
use crate::views::graphics::{GraphicsBackend, GraphicsConfig, load_graphics_config};
|
use crate::views::graphics::{GraphicsBackend, GraphicsConfig, load_graphics_config};
|
||||||
|
|
||||||
|
#[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)]
|
||||||
|
/// A local diffing tool for decompilation projects.
|
||||||
|
struct TopLevel {
|
||||||
|
#[argp(option, short = 'L')]
|
||||||
|
/// Minimum logging level. (Default: info)
|
||||||
|
/// Possible values: error, warn, info, debug, trace
|
||||||
|
log_level: Option<LogLevel>,
|
||||||
|
#[argp(option, short = 'p')]
|
||||||
|
/// Path to the project directory.
|
||||||
|
project_dir: Option<PathBuf>,
|
||||||
|
/// Print version information and exit.
|
||||||
|
#[argp(switch, short = 'V')]
|
||||||
|
version: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn load_icon() -> Result<egui::IconData> {
|
fn load_icon() -> Result<egui::IconData> {
|
||||||
let decoder = png::Decoder::new(include_bytes!("../assets/icon_64.png").as_ref());
|
let decoder = png::Decoder::new(include_bytes!("../assets/icon_64.png").as_ref());
|
||||||
let mut reader = decoder.read_info()?;
|
let mut reader = decoder.read_info()?;
|
||||||
@ -38,23 +103,63 @@ fn load_icon() -> Result<egui::IconData> {
|
|||||||
const APP_NAME: &str = "objdiff";
|
const APP_NAME: &str = "objdiff";
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
// Log to stdout (if you run with `RUST_LOG=debug`).
|
let args: TopLevel = argp_version::from_env();
|
||||||
tracing_subscriber::fmt()
|
let builder = tracing_subscriber::fmt();
|
||||||
.with_env_filter(
|
if let Some(level) = args.log_level {
|
||||||
EnvFilter::builder()
|
builder
|
||||||
// Default to info level
|
.with_max_level(match level {
|
||||||
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
|
LogLevel::Error => LevelFilter::ERROR,
|
||||||
.from_env_lossy()
|
LogLevel::Warn => LevelFilter::WARN,
|
||||||
// This module is noisy at info level
|
LogLevel::Info => LevelFilter::INFO,
|
||||||
.add_directive("wgpu_core::device::resource=warn".parse().unwrap()),
|
LogLevel::Debug => LevelFilter::DEBUG,
|
||||||
)
|
LogLevel::Trace => LevelFilter::TRACE,
|
||||||
.init();
|
})
|
||||||
|
.init();
|
||||||
|
} else {
|
||||||
|
builder
|
||||||
|
.with_env_filter(
|
||||||
|
EnvFilter::builder()
|
||||||
|
// Default to info level
|
||||||
|
.with_default_directive(LevelFilter::INFO.into())
|
||||||
|
.from_env_lossy()
|
||||||
|
// This module is noisy at info level
|
||||||
|
.add_directive("wgpu_core::device::resource=warn".parse().unwrap()),
|
||||||
|
)
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
|
||||||
// Because localtime_r is unsound in multithreaded apps,
|
// Because localtime_r is unsound in multithreaded apps,
|
||||||
// we must call this before initializing eframe.
|
// we must call this before initializing eframe.
|
||||||
// https://github.com/time-rs/time/issues/293
|
// https://github.com/time-rs/time/issues/293
|
||||||
let utc_offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
|
let utc_offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
|
||||||
|
|
||||||
|
// Resolve project directory if provided
|
||||||
|
let project_dir = if let Some(path) = args.project_dir {
|
||||||
|
match path.canonicalize() {
|
||||||
|
Ok(path) => {
|
||||||
|
// Ensure the path is a directory
|
||||||
|
if path.is_dir() {
|
||||||
|
match check_path_buf(path) {
|
||||||
|
Ok(path) => Some(path),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to convert project directory to UTF-8 path: {}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::error!("Project directory is not a directory: {}", path.display());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to canonicalize project directory: {}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let app_path = std::env::current_exe().ok();
|
let app_path = std::env::current_exe().ok();
|
||||||
let exec_path: Rc<Mutex<Option<PathBuf>>> = Rc::new(Mutex::new(None));
|
let exec_path: Rc<Mutex<Option<PathBuf>>> = Rc::new(Mutex::new(None));
|
||||||
let mut native_options = eframe::NativeOptions {
|
let mut native_options = eframe::NativeOptions {
|
||||||
@ -113,6 +218,7 @@ fn main() -> ExitCode {
|
|||||||
app_path.clone(),
|
app_path.clone(),
|
||||||
graphics_config.clone(),
|
graphics_config.clone(),
|
||||||
graphics_config_path.clone(),
|
graphics_config_path.clone(),
|
||||||
|
project_dir.clone(),
|
||||||
) {
|
) {
|
||||||
eframe_error = Some(e);
|
eframe_error = Some(e);
|
||||||
}
|
}
|
||||||
@ -139,6 +245,7 @@ fn main() -> ExitCode {
|
|||||||
app_path.clone(),
|
app_path.clone(),
|
||||||
graphics_config.clone(),
|
graphics_config.clone(),
|
||||||
graphics_config_path.clone(),
|
graphics_config_path.clone(),
|
||||||
|
project_dir.clone(),
|
||||||
) {
|
) {
|
||||||
eframe_error = Some(e);
|
eframe_error = Some(e);
|
||||||
} else {
|
} else {
|
||||||
@ -161,6 +268,7 @@ fn main() -> ExitCode {
|
|||||||
app_path,
|
app_path,
|
||||||
graphics_config,
|
graphics_config,
|
||||||
graphics_config_path,
|
graphics_config_path,
|
||||||
|
project_dir,
|
||||||
) {
|
) {
|
||||||
eframe_error = Some(e);
|
eframe_error = Some(e);
|
||||||
} else {
|
} else {
|
||||||
@ -204,6 +312,7 @@ fn run_eframe(
|
|||||||
app_path: Option<PathBuf>,
|
app_path: Option<PathBuf>,
|
||||||
graphics_config: GraphicsConfig,
|
graphics_config: GraphicsConfig,
|
||||||
graphics_config_path: Option<PathBuf>,
|
graphics_config_path: Option<PathBuf>,
|
||||||
|
project_dir: Option<Utf8PlatformPathBuf>,
|
||||||
) -> Result<(), eframe::Error> {
|
) -> Result<(), eframe::Error> {
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
APP_NAME,
|
APP_NAME,
|
||||||
@ -216,6 +325,7 @@ fn run_eframe(
|
|||||||
app_path,
|
app_path,
|
||||||
graphics_config,
|
graphics_config,
|
||||||
graphics_config_path,
|
graphics_config_path,
|
||||||
|
project_dir,
|
||||||
)))
|
)))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user