use clap::{Parser, Subcommand};
use r2x::commands::{
config::{self, ConfigAction, PythonAction},
init,
log::{self, LogAction},
plugins, read, run,
};
use r2x::common::GlobalOpts;
use r2x_config as config_manager;
use r2x_logger as logger;
#[derive(Parser)]
#[command(name = "r2x")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(
about = "Energy translator framework",
long_about = "R2X is a CLI tool for translating models."
)]
struct Cli {
#[command(flatten)]
global: GlobalOpts,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
#[command(subcommand_required = false, arg_required_else_help = false)]
Config {
#[command(subcommand)]
action: Option<ConfigAction>,
},
Python {
#[command(subcommand)]
action: PythonAction,
},
#[command(subcommand_required = false, arg_required_else_help = false)]
Log {
#[command(subcommand)]
action: Option<LogAction>,
},
List {
plugin: Option<String>,
module: Option<String>,
},
Install {
plugin: Option<String>,
#[arg(short, long)]
editable: bool,
#[arg(long)]
no_cache: bool,
#[arg(long)]
host: Option<String>,
#[arg(long, conflicts_with_all = ["tag", "commit"])]
branch: Option<String>,
#[arg(long, conflicts_with_all = ["branch", "commit"])]
tag: Option<String>,
#[arg(long, conflicts_with_all = ["branch", "tag"])]
commit: Option<String>,
},
Remove { plugin: String },
Sync,
Clean {
#[arg(short = 'y', long)]
yes: bool,
},
Init {
file: Option<String>,
},
Run(run::RunCommand),
Read(read::ReadCommand),
}
fn with_plugin_context<F>(action: F)
where
F: FnOnce(&mut plugins::context::PluginContext) -> Result<(), r2x::plugins::error::PluginError>,
{
let mut ctx = match plugins::context::PluginContext::load() {
Ok(ctx) => ctx,
Err(e) => {
logger::error(&e.to_string());
return;
}
};
if let Err(e) = action(&mut ctx) {
logger::error(&e.to_string());
}
}
fn main() {
let cli = Cli::parse();
let mut startup_config = match config_manager::Config::load() {
Ok(cfg) => Some(cfg),
Err(e) => {
eprintln!("Warning: Failed to load config: {}", e);
None
}
};
let (saved_log_python, saved_no_stdout, saved_log_path, saved_log_max_size) =
match startup_config.as_ref() {
Some(cfg) => (
cfg.log_python.unwrap_or(false),
cfg.no_stdout.unwrap_or(false),
cfg.log_path.as_deref(),
cfg.log_max_size,
),
None => (false, false, None, None),
};
let effective_log_python = cli.global.log_python || saved_log_python;
let effective_no_stdout = cli.global.no_stdout || saved_no_stdout;
if let Err(e) = logger::init_with_config(
cli.global.verbosity_level(),
effective_log_python,
effective_no_stdout,
saved_log_path,
saved_log_max_size,
) {
eprintln!("Warning: Failed to initialize logger: {}", e);
}
if let Some(cfg) = startup_config.as_mut() {
if let Err(e) = cfg.ensure_uv_path().and_then(|_| cfg.ensure_cache_path()) {
logger::warn(&format!("Failed to setup CLI: {}", e));
}
}
match cli.command {
Commands::Config { action } => {
config::handle_config(action, cli.global);
}
Commands::Python { action } => {
config::handle_python(action, cli.global);
}
Commands::Log { action } => {
log::handle_log(action);
}
Commands::List { plugin, module } => {
with_plugin_context(|ctx| {
plugins::list::list_plugins(&cli.global, plugin, module, ctx)
});
}
Commands::Install {
plugin,
editable,
no_cache,
host,
branch,
tag,
commit,
} => match plugin {
Some(pkg) => {
with_plugin_context(|ctx| {
plugins::install::install_plugin(
&pkg,
editable,
no_cache,
plugins::install::GitOptions {
host,
branch,
tag,
commit,
},
ctx,
)
});
}
None => {
if let Err(e) = plugins::install::show_install_help() {
logger::error(&e.to_string());
}
}
},
Commands::Remove { plugin } => {
with_plugin_context(|ctx| plugins::remove::remove_plugin(&plugin, ctx));
}
Commands::Sync => {
with_plugin_context(plugins::sync::sync_manifest);
}
Commands::Clean { yes } => {
with_plugin_context(|ctx| plugins::clean::clean_manifest(yes, ctx));
}
Commands::Init { file } => {
init::handle_init(file, cli.global);
}
Commands::Run(cmd) => {
if let Err(e) = run::handle_run(cmd, cli.global) {
logger::error(&format!("Run command failed: {}", e));
std::process::exit(1);
}
}
Commands::Read(cmd) => {
if let Err(e) = read::handle_read(cmd, cli.global) {
logger::error(&format!("Read command failed: {}", e));
std::process::exit(1);
}
}
}
}