use core::profile::ProfileCommands;
use std::{path::PathBuf, process::ExitCode};
use clap::{Parser, Subcommand, ValueHint, CommandFactory};
use clap_complete::{Shell, generate};
use tracing::{debug, error};
use tracing_subscriber::{EnvFilter, FmtSubscriber};
pub mod config;
mod core;
pub mod model;
pub mod traits;
pub mod utils;
#[macro_use]
mod macros;
use model::ModName;
use utils::validate_modname;
use crate::core::profile;
#[derive(Parser)]
#[clap(name = "Papa")]
#[clap(author = "AnAcutalEmerald <emerald@emeraldgreen.dev>")]
#[clap(about = "Command line mod manager for Northstar")]
#[clap(after_help = "Welcome back. Cockpit cooling reactivated.")]
#[clap(version)]
struct Cli {
#[clap(subcommand)]
command: Commands,
#[clap(global = true, short, long)]
debug: bool,
#[clap(global = true, short = 'C', long = "no-cache")]
no_cache: bool,
}
#[derive(Subcommand)]
enum Commands {
Complete {
#[clap(value_name = "SHELL", value_enum)]
shell: Option<Shell>,
},
Env {},
Export {
#[clap(default_value = "papa.ron")]
file: PathBuf,
},
Import {
#[arg(default_value = "papa.ron", value_hint = ValueHint::FilePath)]
file: PathBuf,
#[clap(short, long)]
yes: bool,
#[clap(short, long)]
force: bool,
},
#[clap(alias = "i")]
Install {
#[clap(value_name = "MOD", value_hint = ValueHint::Other)]
#[clap(help = "Mod name(s) to install")]
#[clap(required_unless_present = "file")]
#[clap(value_parser = validate_modname)]
mod_names: Vec<ModName>,
#[arg(short = 'F', long, value_hint = ValueHint::FilePath)]
file: Option<PathBuf>,
#[clap(short, long)]
yes: bool,
#[clap(short, long)]
force: bool,
#[clap(short, long)]
global: bool,
},
#[clap(alias = "r", alias = "rm")]
Remove {
#[clap(value_name = "MOD", value_hint = ValueHint::Other)]
#[clap(help = "Mod name(s) to remove")]
#[clap(value_parser = validate_modname)]
#[clap(required = true)]
mod_names: Vec<ModName>,
},
#[clap(alias = "l", alias = "ls")]
List {
#[clap(short, long)]
global: bool,
#[clap(short, long)]
all: bool,
},
#[clap(alias = "u")]
Update {
#[clap(short, long)]
yes: bool,
},
#[clap(alias = "s")]
Search {
#[clap(value_hint = ValueHint::Other)]
term: Vec<String>,
},
Disable {
#[clap(value_hint = ValueHint::Other)]
mods: Vec<String>,
#[clap(short, long)]
all: bool,
#[clap(short, long)]
force: bool,
},
Enable {
#[clap(value_hint = ValueHint::Other)]
mods: Vec<String>,
#[arg(short, long)]
all: bool,
},
#[cfg(feature = "northstar")]
#[clap(alias("ns"))]
Northstar {
#[clap(subcommand)]
command: NstarCommands,
},
#[cfg(feature = "launcher")]
#[clap(alias("start"))]
Run {
#[arg(short = 'P', long = "no-profile")]
no_profile: bool,
},
#[clap(alias = "p", alias = "profiles")]
Profile {
#[clap(subcommand)]
command: ProfileCommands,
},
}
#[derive(Subcommand)]
pub enum NstarCommands {
Init {
#[arg(short, long)]
force: bool,
#[arg(value_hint = ValueHint::DirPath)]
path: Option<PathBuf>,
},
Update {},
}
fn main() -> ExitCode {
let cli = Cli::try_parse();
if let Err(e) = cli {
e.exit();
}
let cli = cli.unwrap();
if cli.debug {
std::env::set_var("RUST_LOG", "DEBUG");
}
let subscriber = FmtSubscriber::builder()
.without_time()
.with_env_filter(EnvFilter::from_default_env())
.finish();
tracing::subscriber::set_global_default(subscriber).expect("Unable to init tracing");
debug!("Config: {:#?}", *config::CONFIG);
let res = match cli.command {
Commands::Complete { shell } => {
if let Some(shell) = shell.or_else(Shell::from_env) {
let mut cmd = Cli::command();
let out = std::io::stdout();
generate(shell, &mut cmd, "papa", &mut out.lock());
Ok(())
} else {
eprintln!("Please provide a shell to generate completions for");
Err(anyhow::anyhow!("Unknown shell"))
}
}
Commands::Update { yes } => core::update(yes, cli.no_cache),
Commands::List { global, all } => core::list(global, all),
Commands::Install {
file, yes, force, ..
} if file.is_some() => {
let Some(f) = file else {
return ExitCode::FAILURE;
};
core::import(f, yes, force, cli.no_cache)
}
Commands::Install {
mod_names,
yes,
force,
..
} => core::install(mod_names, yes, force, cli.no_cache),
Commands::Disable { mods, all, force } => {
core::disable(mods.into_iter().collect(), all, force)
}
Commands::Enable { mods, all } => core::enable(mods.into_iter().collect(), all),
Commands::Search { term } => core::search(&term),
Commands::Remove { mod_names } => core::remove(mod_names),
Commands::Import { file, yes, force } => core::import(file, yes, force, cli.no_cache),
Commands::Export { file } => core::export(file),
Commands::Env {} => core::env(),
#[cfg(feature = "northstar")]
Commands::Northstar { command } => core::northstar(&command),
#[cfg(feature = "launcher")]
Commands::Run { no_profile } => core::run(no_profile),
Commands::Profile { command } => profile::handle(&command),
};
if let Err(e) = res {
if cli.debug {
error!("{:#?}", e);
}
eprintln!("{e}");
return ExitCode::FAILURE;
}
ExitCode::SUCCESS
}