use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
const INSTALLER: &[u8] = include_bytes!("../install-jetson.sh");
#[derive(Debug, PartialEq, Eq)]
enum BootstrapAction {
Help,
Version,
Check,
Install {
launch: bool,
forward: Vec<OsString>,
},
}
pub fn run() -> i32 {
let args = env::args_os().skip(1).collect::<Vec<_>>();
match bootstrap_action(&args) {
BootstrapAction::Help => {
print_help();
0
}
BootstrapAction::Version => {
println!("lios {} (installer)", env!("CARGO_PKG_VERSION"));
0
}
BootstrapAction::Check => run_installer(true, false, &[]),
BootstrapAction::Install { launch, forward } => run_installer(false, launch, &forward),
}
}
fn bootstrap_action(args: &[OsString]) -> BootstrapAction {
match args.first().and_then(|arg| arg.to_str()) {
Some("-h" | "--help") => BootstrapAction::Help,
Some("-V" | "--version") => BootstrapAction::Version,
Some("--check") => BootstrapAction::Check,
Some("install" | "--install" | "---install") => BootstrapAction::Install {
launch: false,
forward: Vec::new(),
},
_ => BootstrapAction::Install {
launch: true,
forward: args.to_vec(),
},
}
}
fn run_installer(check_only: bool, launch: bool, forward: &[OsString]) -> i32 {
if !cfg!(target_os = "linux") {
eprintln!("Lios is supported on Linux.");
return 1;
}
let installer_path = installer_path();
let current_executable = env::current_exe().ok();
if let Err(error) = fs::write(&installer_path, INSTALLER) {
eprintln!("Could not prepare the Lios installer: {error}");
return 1;
}
let mut installer = Command::new("sh");
installer.arg(&installer_path);
if check_only {
installer.arg("--check");
}
if let Some(install_root) = current_executable
.as_deref()
.and_then(|executable| executable.parent())
.and_then(|bin| bin.parent())
{
installer.env("LIOS_INSTALL_ROOT", install_root);
}
let status = installer.status();
let _ = fs::remove_file(&installer_path);
let Ok(status) = status else {
eprintln!("Could not start the Lios installer.");
return 1;
};
if !status.success() {
return status.code().unwrap_or(1);
}
if check_only || !launch {
return 0;
}
let Some(executable) = current_executable else {
eprintln!("Lios installed, but the executable path could not be resolved.");
return 1;
};
match Command::new(executable).args(forward).status() {
Ok(status) => status.code().unwrap_or(0),
Err(error) => {
eprintln!("Lios installed, but could not be launched: {error}");
1
}
}
}
fn installer_path() -> PathBuf {
env::temp_dir().join(format!("lios-install-{}.sh", std::process::id()))
}
fn print_help() {
println!(
"Lios Terminal installer\n\nUsage:\n lios\n lios install\n lios --check\n\nThe first run installs native GTK/VTE dependencies, replaces this bootstrap with the full Lios terminal, creates the desktop launcher, and starts Lios."
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_arguments_installs_then_launches() {
assert_eq!(
bootstrap_action(&[]),
BootstrapAction::Install {
launch: true,
forward: Vec::new()
}
);
}
#[test]
fn install_only_provisions_native_binary() {
assert_eq!(
bootstrap_action(&[OsString::from("install")]),
BootstrapAction::Install {
launch: false,
forward: Vec::new()
}
);
}
#[test]
fn launch_arguments_are_forwarded_after_install() {
let args = vec![OsString::from("--theme"), OsString::from("bronco")];
assert_eq!(
bootstrap_action(&args),
BootstrapAction::Install {
launch: true,
forward: args
}
);
}
}