Files
aare_launcher/src/main.rs
2026-07-24 11:13:29 +02:00

92 lines
2.7 KiB
Rust

mod config;
mod detect;
mod launcher;
mod tui;
use std::path::{Path, PathBuf};
use anyhow::{Context, Error, Result, bail};
use clap::{Parser, Subcommand};
use config::{Config, LauncherEntry, load_config};
use detect::detect_beamline;
#[derive(Parser)]
#[command(name = "aarel")]
struct Cli {
/// Path to the launcher config file.
#[arg(long, default_value = "data/defaults.json")]
config: PathBuf,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
/// Enter the TUI to choose what to launch
#[command(alias = "p")]
Pick,
/// Launch the AareGUI with default settings
#[command(alias = "g")]
Gui {
/// Config entry (beamline) to launch. Defaults to the detected beamline.
#[arg(long)]
entry: Option<String>,
},
/// Launch the AareDAQ server with default settings
#[command(alias = "s")]
Server {
/// Config entry (beamline) to launch. Defaults to the detected beamline.
#[arg(long)]
entry: Option<String>,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
let json = std::fs::read_to_string(&cli.config)
.with_context(|| format!("failed to read config file {}", cli.config.display()))?;
let config = load_config(&json)?;
check_base_exists("deployments_base", &config.deployments_base, &cli.config)?;
check_base_exists("venvs_base", &config.deployments_base, &cli.config)?;
match cli.command {
Command::Pick => {
if let Some((python, entrypoint)) = tui::run(&config)? {
launcher::launch(&python, &entrypoint)?;
}
}
Command::Gui { entry } => launcher::launch_gui(lookup_entry(&config, entry)?)?,
Command::Server { entry } => launcher::launch_server(lookup_entry(&config, entry)?)?,
}
Ok(())
}
/// Fail with a helpful message if a configured base directory is missing.
fn check_base_exists(field: &str, base: &Path, config_file: &Path) -> Result<()> {
if !base.is_dir() {
bail!(
"{field} '{}' does not exist — edit '{field}' in {} to point at a valid directory",
base.display(),
config_file.display(),
);
}
Ok(())
}
/// Look up the launcher entry to use: the explicitly requested `entry`, or the
/// detected beamline when none was given.
fn lookup_entry(config: &Config, entry: Option<String>) -> Result<&LauncherEntry> {
let beamline = match entry {
Some(entry) => entry,
None => detect_beamline().map_err(Error::msg)?,
};
config
.entries
.get(&beamline)
.with_context(|| format!("no config entry for beamline '{beamline}'"))
}