mod commands;
mod shell;
use std::sync::atomic::{AtomicBool, Ordering};
use clap::{Parser, Subcommand};
use color_eyre::eyre::Result;
use futures::future::{self, Either};
use commands::{build, clean, create, devices, doctor, package, run};
static CANCELLED: AtomicBool = AtomicBool::new(false);
fn set_cancelled() {
CANCELLED.store(true, Ordering::SeqCst);
}
fn is_cancelled() -> bool {
CANCELLED.load(Ordering::SeqCst)
}
#[derive(Parser, Debug)]
#[command(name = "water", version, about, long_about = None)]
struct Cli {
#[arg(long, global = true)]
json: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
Create(create::Args),
Run(run::Args),
Build(build::Args),
Package(package::Args),
Clean(clean::Args),
Doctor(doctor::Args),
Devices(devices::Args),
}
fn main() -> Result<()> {
color_eyre::config::HookBuilder::default()
.display_location_section(false)
.display_env_section(false)
.install()?;
let cli = Cli::parse();
shell::init(cli.json);
ctrlc::set_handler(set_cancelled).expect("failed to set Ctrl+C handler");
smol::block_on(async {
let ctrl_c_future = async {
loop {
if is_cancelled() {
return;
}
smol::Timer::after(std::time::Duration::from_millis(50)).await;
}
};
let command = async {
match cli.command {
Commands::Create(args) => create::run(args).await,
Commands::Run(args) => run::run(args).await,
Commands::Build(args) => build::run(args).await,
Commands::Package(args) => package::run(args).await,
Commands::Clean(args) => clean::run(args).await,
Commands::Doctor(args) => doctor::run(args).await,
Commands::Devices(args) => devices::run(args).await,
}
};
let command = std::pin::pin!(command);
let cancel = std::pin::pin!(ctrl_c_future);
match future::select(command, cancel).await {
Either::Left((result, _)) => {
if is_cancelled() {
Ok(())
} else {
result
}
}
Either::Right(((), _)) => {
Ok(())
}
}
})
}