131 lines
4.7 KiB
Rust
131 lines
4.7 KiB
Rust
//! The standalone TUI binary: a clap-derive CLI ([`cbd_cli::TuiCli`]). With
|
|
//! no subcommand it loads the client config (writing defaults on first run),
|
|
//! applies the `--address/--user/--password/--spectrum` overrides, and runs
|
|
//! the TUI — exactly as before. Subcommands cover `auth` (write credentials
|
|
//! into the config), remote control (`library`/`queue`/`global`), and shell
|
|
//! completions. All client logic lives in the library so the bundled `cbd`
|
|
//! binary can host it too (architecture/cbd-bundle.md D1).
|
|
|
|
use std::sync::OnceLock;
|
|
|
|
use cbd_cli::{RemoteCmd, TuiCli, TuiCommand};
|
|
use cbd_tui::config::{self, Config};
|
|
use clap::{CommandFactory, Parser};
|
|
|
|
/// The config file name for the standalone TUI.
|
|
const CONFIG_FILE: &str = "cbd-tui.toml";
|
|
|
|
static CONFIG: OnceLock<Config> = OnceLock::new();
|
|
|
|
/// Logs to a file: the terminal is owned by the TUI, so writing log lines to
|
|
/// stdout/stderr would corrupt the interface.
|
|
fn init_tracing() -> Option<tracing_appender::non_blocking::WorkerGuard> {
|
|
use tracing_subscriber::{prelude::*, EnvFilter};
|
|
|
|
let log_dir = dirs::state_dir()
|
|
.or_else(dirs::cache_dir)
|
|
.unwrap_or_else(std::env::temp_dir)
|
|
.join("crabidy");
|
|
if let Err(err) = std::fs::create_dir_all(&log_dir) {
|
|
eprintln!(
|
|
"could not create log directory {}: {err}",
|
|
log_dir.display()
|
|
);
|
|
return None;
|
|
}
|
|
let file_appender = tracing_appender::rolling::daily(&log_dir, "cbd-tui.log");
|
|
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
|
|
let env_filter = EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| EnvFilter::new("info,cbd_tui=debug,crabidy_core=debug"));
|
|
tracing_subscriber::registry()
|
|
.with(env_filter)
|
|
.with(
|
|
tracing_subscriber::fmt::layer()
|
|
.with_writer(non_blocking)
|
|
.with_ansi(false)
|
|
.with_target(true),
|
|
)
|
|
.init();
|
|
Some(guard)
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let cli = TuiCli::parse();
|
|
match cli.command {
|
|
// No subcommand: load config, apply overrides, run the TUI.
|
|
None => {
|
|
let _log_guard = init_tracing();
|
|
let mut config = config::load_first_run(CONFIG_FILE);
|
|
config::apply_overrides(
|
|
&mut config,
|
|
cli.remote.address,
|
|
cli.remote.user,
|
|
cli.remote.password,
|
|
cli.spectrum,
|
|
);
|
|
let config = CONFIG.get_or_init(|| config);
|
|
cbd_tui::run(config).await
|
|
}
|
|
// Subcommands are one-shot CLI actions; a failure prints a short
|
|
// message and exits non-zero.
|
|
Some(command) => {
|
|
if let Err(err) = run_command(&cli.remote, command).await {
|
|
eprintln!("error: {err}");
|
|
std::process::exit(1);
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Dispatches a TUI subcommand.
|
|
async fn run_command(
|
|
remote: &cbd_cli::RemoteArgs,
|
|
command: TuiCommand,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
match command {
|
|
TuiCommand::Auth(args) => {
|
|
let password = args
|
|
.password
|
|
.ok_or("missing password (pass it as an argument)")?;
|
|
let path = config::write_auth(
|
|
CONFIG_FILE,
|
|
args.role.user_name(),
|
|
&password,
|
|
args.address.as_deref(),
|
|
)?;
|
|
println!(
|
|
"wrote credentials for {} to {}",
|
|
args.role.user_name(),
|
|
path.display()
|
|
);
|
|
Ok(())
|
|
}
|
|
TuiCommand::Library(cmd) => {
|
|
cbd_cli::run_remote(&connection(remote), RemoteCmd::Library(cmd)).await
|
|
}
|
|
TuiCommand::Queue(cmd) => {
|
|
cbd_cli::run_remote(&connection(remote), RemoteCmd::Queue(cmd)).await
|
|
}
|
|
TuiCommand::Global(cmd) => {
|
|
cbd_cli::run_remote(&connection(remote), RemoteCmd::Global(cmd)).await
|
|
}
|
|
TuiCommand::Completions(args) => {
|
|
cbd_cli::print_completions(args.shell, &mut TuiCli::command(), "cbd-tui");
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Resolves the connection for a remote command: the CLI flags win over the
|
|
/// client config, which supplies the fallback (address and credentials).
|
|
fn connection(remote: &cbd_cli::RemoteArgs) -> cbd_cli::Connection {
|
|
let config = config::load_first_run(CONFIG_FILE);
|
|
cbd_cli::Connection {
|
|
address: remote.address.clone().unwrap_or(config.server.address),
|
|
user: remote.user.clone().unwrap_or(config.server.user),
|
|
password: remote.password.clone().unwrap_or(config.server.password),
|
|
}
|
|
}
|