From b6dd9e78d42b9fe90bfba456bc859476860b8925 Mon Sep 17 00:00:00 2001 From: RGBCube Date: Wed, 4 Jun 2025 21:09:35 +0300 Subject: [PATCH] package: make multicall --- build.rs | 51 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 63 +++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..5cc203d --- /dev/null +++ b/build.rs @@ -0,0 +1,51 @@ +use std::env; +use std::fs; +use std::path::PathBuf; + +const MULTICALL_NAMES: &[&str] = &["cpu", "power"]; + +fn main() -> Result<(), Box> { + println!("cargo:rerun-if-changed=build.rs"); + + let out_dir = PathBuf::from(env::var("OUT_DIR")?); + let target = out_dir + .parent() // target/debug/build/-/out + .and_then(|p| p.parent()) // target/debug/build/- + .and_then(|p| p.parent()) // target/debug/ + .ok_or("failed to find target directory")?; + + let main_binary_name = env::var("CARGO_PKG_NAME")?; + + let main_binary_path = target.join(&main_binary_name); + + let mut errored = false; + + for name in MULTICALL_NAMES { + let hardlink_path = target.join(name); + + if hardlink_path.exists() { + if hardlink_path.is_dir() { + fs::remove_dir_all(&hardlink_path)?; + } else { + fs::remove_file(&hardlink_path)?; + } + } + + if let Err(error) = fs::hard_link(&main_binary_path, &hardlink_path) { + println!( + "cargo:warning=failed to create hard link '{path}': {error}", + path = hardlink_path.display(), + ); + errored = true; + } + } + + if errored { + println!( + "cargo:warning=this often happens because the target binary isn't built yet, try running `cargo build` again" + ); + println!("cargo:warning=keep in mind that this is for development purposes only"); + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index e435cee..5b56d92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,30 +21,52 @@ use yansi::Paint as _; #[derive(clap::Parser, Debug)] #[clap(author, version, about)] struct Cli { - #[command(flatten)] - verbosity: clap_verbosity_flag::Verbosity, - #[clap(subcommand)] command: Command, } #[derive(clap::Parser, Debug)] +#[clap(multicall = true)] enum Command { - /// Display information. - Info, + /// Watt daemon. + Watt { + #[command(flatten)] + verbosity: clap_verbosity_flag::Verbosity, - /// Start the daemon. - Start { /// The daemon config path. #[arg(long, env = "WATT_CONFIG")] config: PathBuf, }, - /// Modify CPU attributes. - CpuSet(config::CpuDelta), + /// CPU metadata and modification utility. + Cpu { + #[command(flatten)] + verbosity: clap_verbosity_flag::Verbosity, + #[clap(subcommand)] + command: CpuCommand, + }, + + /// Power supply metadata and modification utility. + Power { + #[command(flatten)] + verbosity: clap_verbosity_flag::Verbosity, + + #[clap(subcommand)] + command: PowerCommand, + }, +} + +#[derive(clap::Parser, Debug)] +enum CpuCommand { + /// Modify CPU attributes. + Set(config::CpuDelta), +} + +#[derive(clap::Parser, Debug)] +enum PowerCommand { /// Modify power supply attributes. - PowerSet(config::PowerDelta), + Set(config::PowerDelta), } fn real_main() -> anyhow::Result<()> { @@ -52,24 +74,33 @@ fn real_main() -> anyhow::Result<()> { yansi::whenever(yansi::Condition::TTY_AND_COLOR); + let (Command::Watt { verbosity, .. } + | Command::Cpu { verbosity, .. } + | Command::Power { verbosity, .. }) = cli.command; + env_logger::Builder::new() - .filter_level(cli.verbosity.log_level_filter()) + .filter_level(verbosity.log_level_filter()) .format_timestamp(None) .format_module_path(false) .init(); match cli.command { - Command::Info => todo!(), - - Command::Start { config } => { + Command::Watt { config, .. } => { let config = config::DaemonConfig::load_from(&config) .context("failed to load daemon config file")?; daemon::run(config) } - Command::CpuSet(delta) => delta.apply(), - Command::PowerSet(delta) => delta.apply(), + Command::Cpu { + command: CpuCommand::Set(delta), + .. + } => delta.apply(), + + Command::Power { + command: PowerCommand::Set(delta), + .. + } => delta.apply(), } }