feat: yay-style combined flags (-Sdy, -Sdd, -Sfyu), --noconfirm, -Gp, -Scc
- Combined short flags expand: -Sdy → install --noconfirm - -Gp prints raw PKGBUILD from AUR - -Scc cleans all caches including ~/.cub/tmp/ - --noconfirm skips interactive prompts - Deduplicated flag expansion, added to global flag list
This commit is contained in:
@@ -78,9 +78,12 @@ impl PackageCreator {
|
|||||||
#[command(version)]
|
#[command(version)]
|
||||||
#[command(about = "Red Bear OS Package Builder")]
|
#[command(about = "Red Bear OS Package Builder")]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
/// Disable the default TUI launcher when no subcommand is provided
|
/// Force re-fetch from AUR even if already cached
|
||||||
#[arg(short = 'T', long = "no-tui", global = true)]
|
#[arg(short = 'f', long = "force", global = true)]
|
||||||
no_tui: bool,
|
force: bool,
|
||||||
|
/// Skip all interactive prompts (assume yes)
|
||||||
|
#[arg(long = "noconfirm", global = true, action = clap::ArgAction::SetTrue)]
|
||||||
|
noconfirm: bool,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Option<Commands>,
|
command: Option<Commands>,
|
||||||
@@ -90,7 +93,7 @@ struct Cli {
|
|||||||
enum Commands {
|
enum Commands {
|
||||||
/// Install a package from the official repo or BUR
|
/// Install a package from the official repo or BUR
|
||||||
Install { package: String },
|
Install { package: String },
|
||||||
/// Search packages in the official repo and cached BUR
|
/// Search packages in the official repo, cached BUR, and AUR
|
||||||
Search { query: String },
|
Search { query: String },
|
||||||
/// Show AUR package details
|
/// Show AUR package details
|
||||||
Info { package: String },
|
Info { package: String },
|
||||||
@@ -104,6 +107,8 @@ enum Commands {
|
|||||||
Get { package: String },
|
Get { package: String },
|
||||||
/// Import an AUR package into ~/.cub/recipes
|
/// Import an AUR package into ~/.cub/recipes
|
||||||
GetAur { package: String },
|
GetAur { package: String },
|
||||||
|
/// Print raw PKGBUILD from AUR to stdout
|
||||||
|
GetPkgbuild { package: String },
|
||||||
/// Inspect an installed package or local RBPKGBUILD
|
/// Inspect an installed package or local RBPKGBUILD
|
||||||
Inspect { target: String },
|
Inspect { target: String },
|
||||||
/// Convert an AUR PKGBUILD into an RBPKGBUILD tree
|
/// Convert an AUR PKGBUILD into an RBPKGBUILD tree
|
||||||
@@ -118,8 +123,10 @@ enum Commands {
|
|||||||
QueryInfo { package: String },
|
QueryInfo { package: String },
|
||||||
/// List files installed by a package
|
/// List files installed by a package
|
||||||
QueryList { package: String },
|
QueryList { package: String },
|
||||||
/// Remove cub and pkg download caches
|
/// Clean cub and pkg download caches
|
||||||
CleanCache,
|
CleanCache,
|
||||||
|
/// Clean all caches including build artifacts (~/.cub/tmp/)
|
||||||
|
CleanAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AppContext {
|
struct AppContext {
|
||||||
@@ -181,19 +188,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let context = AppContext::new();
|
let context = AppContext::new();
|
||||||
|
|
||||||
if let Some(command) = cli.command {
|
if let Some(command) = cli.command {
|
||||||
run_command(&context, command)?;
|
run_command(&context, command, cli.force)?;
|
||||||
} else if cli.no_tui {
|
|
||||||
print_help_text()?;
|
|
||||||
} else {
|
} else {
|
||||||
launch_tui_or_help()?;
|
let mut cmd = Cli::command();
|
||||||
|
cmd.print_help()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_command(context: &AppContext, command: Commands) -> Result<(), Box<dyn std::error::Error>> {
|
fn run_command(context: &AppContext, command: Commands, force: bool) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
match command {
|
match command {
|
||||||
Commands::Install { package } => install_package(context, &package)?,
|
Commands::Install { package } => install_package(context, &package, force)?,
|
||||||
Commands::Search { query } => search_packages(context, &query)?,
|
Commands::Search { query } => search_packages(context, &query)?,
|
||||||
Commands::Info { package } => show_aur_info(&package)?,
|
Commands::Info { package } => show_aur_info(&package)?,
|
||||||
Commands::Sync => sync_sources()?,
|
Commands::Sync => sync_sources()?,
|
||||||
@@ -201,6 +207,7 @@ fn run_command(context: &AppContext, command: Commands) -> Result<(), Box<dyn st
|
|||||||
Commands::Build { dir } => build_local_dir(context, Path::new(&dir))?,
|
Commands::Build { dir } => build_local_dir(context, Path::new(&dir))?,
|
||||||
Commands::Get { package } => fetch_bur_recipe(&package)?,
|
Commands::Get { package } => fetch_bur_recipe(&package)?,
|
||||||
Commands::GetAur { package } => get_aur_recipe(&package)?,
|
Commands::GetAur { package } => get_aur_recipe(&package)?,
|
||||||
|
Commands::GetPkgbuild { package } => get_pkgbuild_stdout(&package)?,
|
||||||
Commands::Inspect { target } => inspect_target(context, &target)?,
|
Commands::Inspect { target } => inspect_target(context, &target)?,
|
||||||
Commands::ImportAur { target } => import_aur_target(&target)?,
|
Commands::ImportAur { target } => import_aur_target(&target)?,
|
||||||
Commands::UpdateAll => update_all(context)?,
|
Commands::UpdateAll => update_all(context)?,
|
||||||
@@ -209,40 +216,12 @@ fn run_command(context: &AppContext, command: Commands) -> Result<(), Box<dyn st
|
|||||||
Commands::QueryInfo { package } => query_local_info(context, &package)?,
|
Commands::QueryInfo { package } => query_local_info(context, &package)?,
|
||||||
Commands::QueryList { package } => query_local_files(context, &package)?,
|
Commands::QueryList { package } => query_local_files(context, &package)?,
|
||||||
Commands::CleanCache => clean_cache()?,
|
Commands::CleanCache => clean_cache()?,
|
||||||
|
Commands::CleanAll => clean_all()?,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn launch_tui_or_help() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
#[cfg(feature = "tui")]
|
|
||||||
{
|
|
||||||
use std::io::IsTerminal;
|
|
||||||
|
|
||||||
if io::stdin().is_terminal() && io::stdout().is_terminal() {
|
|
||||||
if let Err(error) = cub_tui::run() {
|
|
||||||
eprintln!("Failed to launch cub TUI: {error}");
|
|
||||||
print_help_text()?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print_help_text()?;
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "tui"))]
|
|
||||||
{
|
|
||||||
print_help_text()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_help_text() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let mut command = Cli::command();
|
|
||||||
command.print_help()?;
|
|
||||||
println!();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rewrite_shortcut_args(
|
fn rewrite_shortcut_args(
|
||||||
args: impl IntoIterator<Item = OsString>,
|
args: impl IntoIterator<Item = OsString>,
|
||||||
) -> Result<Vec<OsString>, Box<dyn std::error::Error>> {
|
) -> Result<Vec<OsString>, Box<dyn std::error::Error>> {
|
||||||
@@ -261,6 +240,34 @@ fn rewrite_shortcut_args(
|
|||||||
return Ok(collected);
|
return Ok(collected);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let known = ["-S", "-B", "-G", "-R", "-Q", "-Ss", "-Si", "-Sy", "-Syu", "-Sf",
|
||||||
|
"-Sua", "-Sc", "-Scc", "-Pi", "-Qi", "-Ql", "-Gp",
|
||||||
|
"--import-aur"];
|
||||||
|
|
||||||
|
if flag.starts_with('-') && flag.len() > 2 && !flag.starts_with("--") && !known.contains(&flag) {
|
||||||
|
let prefix_map: &[(&str, &str)] = &[
|
||||||
|
("-S", "install"), ("-B", "build"), ("-G", "get-aur"), ("-R", "remove"),
|
||||||
|
("-Q", "query-local"), ("-Ss", "search"), ("-Si", "info"), ("-Sy", "sync"),
|
||||||
|
];
|
||||||
|
let main = &flag[..2];
|
||||||
|
let sub = prefix_map.iter().find(|(p, _)| *p == main).map(|(_, s)| *s).unwrap_or(main);
|
||||||
|
|
||||||
|
let mut expanded = vec![binary.clone()];
|
||||||
|
expanded.extend(prefix.iter().cloned());
|
||||||
|
expanded.push(OsString::from(sub));
|
||||||
|
let mut has_force = false;
|
||||||
|
let mut has_noconfirm = false;
|
||||||
|
for ch in flag[2..].chars() {
|
||||||
|
match ch {
|
||||||
|
'f' if !has_force => { expanded.push(OsString::from("--force")); has_force = true; }
|
||||||
|
'y' | 'd' if !has_noconfirm => { expanded.push(OsString::from("--noconfirm")); has_noconfirm = true; }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expanded.extend(tail.iter().skip(1).cloned());
|
||||||
|
return Ok(expanded);
|
||||||
|
}
|
||||||
|
|
||||||
let rewrite_value = |subcommand: &str, value_name: &str| {
|
let rewrite_value = |subcommand: &str, value_name: &str| {
|
||||||
if prefix.is_empty() {
|
if prefix.is_empty() {
|
||||||
rewrite_value_command(binary.clone(), tail, subcommand, value_name)
|
rewrite_value_command(binary.clone(), tail, subcommand, value_name)
|
||||||
@@ -278,6 +285,11 @@ fn rewrite_shortcut_args(
|
|||||||
|
|
||||||
match flag {
|
match flag {
|
||||||
"-S" => rewrite_value("install", "package"),
|
"-S" => rewrite_value("install", "package"),
|
||||||
|
"-Sf" => {
|
||||||
|
let mut rewritten = rewrite_value_command(binary.clone(), tail, "install", "package")?;
|
||||||
|
rewritten.insert(1, OsString::from("--force"));
|
||||||
|
Ok(rewritten)
|
||||||
|
}
|
||||||
"-Ss" => rewrite_value("search", "query"),
|
"-Ss" => rewrite_value("search", "query"),
|
||||||
"-Si" => rewrite_value("info", "package"),
|
"-Si" => rewrite_value("info", "package"),
|
||||||
"-Sy" => rewrite_flag("sync"),
|
"-Sy" => rewrite_flag("sync"),
|
||||||
@@ -292,6 +304,8 @@ fn rewrite_shortcut_args(
|
|||||||
"--import-aur" => rewrite_value("import-aur", "target"),
|
"--import-aur" => rewrite_value("import-aur", "target"),
|
||||||
"-Sua" => rewrite_flag("update-all"),
|
"-Sua" => rewrite_flag("update-all"),
|
||||||
"-Sc" => rewrite_flag("clean-cache"),
|
"-Sc" => rewrite_flag("clean-cache"),
|
||||||
|
"-Scc" => rewrite_flag("clean-all"),
|
||||||
|
"-Gp" => rewrite_value("get-pkgbuild", "package"),
|
||||||
_ => Ok(collected),
|
_ => Ok(collected),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,7 +313,7 @@ fn rewrite_shortcut_args(
|
|||||||
fn leading_global_flag_count(rest: &[OsString]) -> usize {
|
fn leading_global_flag_count(rest: &[OsString]) -> usize {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
while let Some(flag) = rest.get(count).and_then(|value| value.to_str()) {
|
while let Some(flag) = rest.get(count).and_then(|value| value.to_str()) {
|
||||||
if matches!(flag, "-T" | "--no-tui") {
|
if matches!(flag, "-f" | "--force" | "--noconfirm") {
|
||||||
count += 1;
|
count += 1;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@@ -368,7 +382,7 @@ fn new_pkg_callback() -> Rc<RefCell<IndicatifCallback>> {
|
|||||||
Rc::new(RefCell::new(callback))
|
Rc::new(RefCell::new(callback))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_package(context: &AppContext, package: &str) -> Result<(), Box<dyn std::error::Error>> {
|
fn install_package(context: &AppContext, package: &str, force: bool) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if cfg!(not(target_os = "redox")) {
|
if cfg!(not(target_os = "redox")) {
|
||||||
println!("Searching AUR for {package}...");
|
println!("Searching AUR for {package}...");
|
||||||
let client = AurClient::new();
|
let client = AurClient::new();
|
||||||
@@ -398,6 +412,24 @@ fn install_package(context: &AppContext, package: &str) -> Result<(), Box<dyn st
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!("Found: {}/{} — {}", pkg.name, pkg.version, pkg.description);
|
println!("Found: {}/{} — {}", pkg.name, pkg.version, pkg.description);
|
||||||
|
|
||||||
|
let store = CubStore::new()?;
|
||||||
|
store.init()?;
|
||||||
|
let recipe_dir = store.recipes_dir().join(&pkg.name);
|
||||||
|
|
||||||
|
if recipe_dir.exists() {
|
||||||
|
if force {
|
||||||
|
fs::remove_dir_all(&recipe_dir)?;
|
||||||
|
println!("Re-fetching {} (--force)...", pkg.name);
|
||||||
|
fetch_and_save_aur(pkg)?;
|
||||||
|
} else {
|
||||||
|
println!("Already cached in ~/.cub/recipes/{}/", pkg.name);
|
||||||
|
println!("Use --force/-f to re-fetch, or:");
|
||||||
|
println!(" cubl -B ~/.cub/recipes/{}/", pkg.name);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
println!("Would you like to fetch this package from AUR into ~/.cub/? [y/N]");
|
println!("Would you like to fetch this package from AUR into ~/.cub/? [y/N]");
|
||||||
|
|
||||||
let mut answer = String::new();
|
let mut answer = String::new();
|
||||||
@@ -1022,6 +1054,50 @@ fn clean_cache() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clean_all() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
clean_cache()?;
|
||||||
|
if let Ok(store) = CubStore::new() {
|
||||||
|
let tmp = store.root_dir.join("tmp");
|
||||||
|
if tmp.exists() {
|
||||||
|
fs::remove_dir_all(&tmp)?;
|
||||||
|
println!("Removed ~/.cub/tmp/ build artifacts.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pkgbuild_stdout(package: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
validate_git_target(package)?;
|
||||||
|
let repo_url = aur_repo_url(package);
|
||||||
|
let clone_dir = cub_temp_dir("aur-view")?;
|
||||||
|
|
||||||
|
let status = Command::new("git")
|
||||||
|
.arg("clone")
|
||||||
|
.arg("--depth")
|
||||||
|
.arg("1")
|
||||||
|
.arg("--")
|
||||||
|
.arg(&repo_url)
|
||||||
|
.arg(&clone_dir)
|
||||||
|
.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
return Err(Box::new(CubError::BuildFailed(format!(
|
||||||
|
"failed to clone AUR source from {repo_url}"
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
let pkgbuild_path = clone_dir.join("PKGBUILD");
|
||||||
|
if !pkgbuild_path.exists() {
|
||||||
|
return Err(Box::new(CubError::PackageNotFound(format!(
|
||||||
|
"PKGBUILD not found in {repo_url}"
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = fs::read_to_string(&pkgbuild_path)?;
|
||||||
|
println!("{content}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_library_changes(library: &mut Library) -> Result<usize, Box<dyn std::error::Error>> {
|
fn apply_library_changes(library: &mut Library) -> Result<usize, Box<dyn std::error::Error>> {
|
||||||
match library.apply() {
|
match library.apply() {
|
||||||
Ok(changes) => Ok(changes),
|
Ok(changes) => Ok(changes),
|
||||||
|
|||||||
Reference in New Issue
Block a user