//! The `sausage` binary: subcommands only, each delegates to its module. use clap::{Parser, Subcommand}; use std::path::PathBuf; #[derive(Parser)] #[command(version, about)] struct Cli { /// Shared host-user compile cache (otherwise SAUSAGE_CACHE or the XDG cache folder). #[arg(long, global = true)] cache: Option, #[command(subcommand)] command: Command, } #[derive(Subcommand)] enum Command { /// Start a world: the harness, its services and a console. Run { world: PathBuf, #[arg(long)] userland: Option, }, /// Run a shell in a world. With `-c`, run one command and exit with its status. Sh { world: PathBuf, #[arg(short = 'c')] command: Option, #[arg(long)] userland: Option, }, } #[tokio::main] async fn main() -> anyhow::Result<()> { let cli = Cli::parse(); if let Some(cache) = cli.cache { sausage::process::configure_cache(cache)?; } match cli.command { Command::Run { world, userland } => { let bundle = userland.or_else(|| std::env::var_os("SAUSAGE_USERLAND").map(PathBuf::from)); sausage::harness::run_with_userland(&world, bundle.as_deref()).await } Command::Sh { world, command, userland, } => { use sausage::exec::{Command, Exec, Options, Stream}; let bundle = userland.or_else(|| std::env::var_os("SAUSAGE_USERLAND").map(PathBuf::from)); let world = sausage::world::World::open_with_userland(&world, bundle.as_deref())?; let runtime = sausage::process::Runtime::new(world)?; let script = command.ok_or_else(|| anyhow::anyhow!("interactive shell is not implemented"))?; let status = runtime .exec( Command::Shell(script), Options { cwd: "/".into(), ..Default::default() }, &mut |stream, bytes| { use std::io::Write; match stream { Stream::Stdout => { let _ = std::io::stdout().write_all(bytes); } Stream::Stderr => { let _ = std::io::stderr().write_all(bytes); } } }, ) .await?; std::process::exit(status); } } }