mod commands;
mod project_path;
mod shell;
mod toolchain_checks;
use std::sync::{
Arc,
atomic::{AtomicBool, Ordering},
};
use clap::{Parser, Subcommand};
use color_eyre::eyre::Result;
use futures::future::{self, Either};
use tracing_subscriber::EnvFilter;
use commands::{
backend, bench, build, clean, create, device, devices, doctor, gc, inspector, package, preview,
run,
};
#[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)]
#[allow(
clippy::large_enum_variant,
reason = "clap subcommand enum constructed once per CLI invocation; variant size is immaterial"
)]
enum Commands {
Create(create::Args),
Backend(backend::Args),
Run(run::Args),
Bench(bench::Args),
Build(build::Args),
Package(package::Args),
Clean(clean::Args),
Doctor(doctor::Args),
Device(device::Args),
Devices(devices::Args),
Gc(gc::Args),
Preview(preview::Args),
Inspector(inspector::Args),
}
fn main() -> Result<()> {
color_eyre::config::HookBuilder::default()
.display_location_section(false)
.display_env_section(false)
.install()?;
init_cli_tracing();
let cli = Cli::parse();
let shell = shell::Shell::new(cli.json);
let cancelled = Arc::new(AtomicBool::new(false));
{
let cancelled = Arc::clone(&cancelled);
ctrlc::set_handler(move || {
cancelled.store(true, Ordering::SeqCst);
})
.expect("failed to set Ctrl+C handler");
}
smol::block_on({
let cancelled = Arc::clone(&cancelled);
async move {
waterui_cli::water_dir::ensure_global_config().await?;
let ctrl_c_future = async {
loop {
if cancelled.load(Ordering::SeqCst) {
return;
}
smol::Timer::after(std::time::Duration::from_millis(50)).await;
}
};
let command = async {
match cli.command {
Commands::Create(args) => create::run(&shell, args).await,
Commands::Backend(args) => backend::run(&shell, args).await,
Commands::Run(args) => run::run(&shell, args).await,
Commands::Bench(args) => bench::run(&shell, args).await,
Commands::Build(args) => build::run(&shell, args).await,
Commands::Package(args) => package::run(&shell, args).await,
Commands::Clean(args) => clean::run(&shell, args).await,
Commands::Doctor(args) => doctor::run(&shell, args).await,
Commands::Device(args) => device::run(&shell, args).await,
Commands::Devices(args) => devices::run(&shell, args).await,
Commands::Gc(args) => gc::run(&shell, args).await,
Commands::Preview(args) => preview::run(&shell, args).await,
Commands::Inspector(args) => inspector::run(&shell, args).await,
}
};
let command = std::pin::pin!(command);
let cancel = std::pin::pin!(ctrl_c_future);
let result = match future::select(command, cancel).await {
Either::Left((result, _)) => {
if cancelled.load(Ordering::SeqCst) {
Ok(())
} else {
result
}
}
Either::Right(((), _)) => {
Ok(())
}
};
shell.clear();
result
}
})
}
fn init_cli_tracing() {
if std::env::var_os("RUST_LOG").is_none() {
return;
}
let _ = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_target(false)
.with_writer(std::io::stderr)
.try_init();
}