fix: cubl AUR search on Linux, graceful pkgutils fallback

- search now queries AUR directly on Linux (skips pkgutils repo)
- install -S offers AUR fetch + recipe conversion on Linux
- update-all, remove, query-* show helpful error on Linux
- host_only_notice() helper for consistent messaging
This commit is contained in:
2026-05-08 00:47:51 +01:00
parent 44ed766291
commit 2b984db9a1
@@ -369,6 +369,27 @@ fn new_pkg_callback() -> Rc<RefCell<IndicatifCallback>> {
} }
fn install_package(context: &AppContext, package: &str) -> Result<(), Box<dyn std::error::Error>> { fn install_package(context: &AppContext, package: &str) -> Result<(), Box<dyn std::error::Error>> {
if cfg!(not(target_os = "redox")) {
println!("Searching AUR for {package}...");
let client = AurClient::new();
let results = client.search(package, Some("name"))?;
if results.is_empty() {
return Err(format!("{package} not found in AUR").into());
}
let pkg = &results[0];
println!("Found: {}/{}{}", pkg.name, pkg.version, pkg.description);
println!("Would you like to fetch this package from AUR into ~/.cub/? [y/N]");
let mut answer = String::new();
io::stdin().read_line(&mut answer)?;
if answer.trim().to_ascii_lowercase().starts_with('y') {
fetch_aur_to_store(&pkg.name)?;
println!("Recipe saved to ~/.cub/recipes/{}/", pkg.name);
println!("Build with: cubl -B ~/.cub/recipes/{}/", pkg.name);
}
return Ok(());
}
let package_name = PackageName::new(package.to_string())?; let package_name = PackageName::new(package.to_string())?;
let mut library = context.open_library()?; let mut library = context.open_library()?;
@@ -394,22 +415,21 @@ fn install_package(context: &AppContext, package: &str) -> Result<(), Box<dyn st
} }
fn search_packages(context: &AppContext, query: &str) -> Result<(), Box<dyn std::error::Error>> { fn search_packages(context: &AppContext, query: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut library = context.open_library()?; if cfg!(target_os = "redox") {
let official_matches = library.search(query)?; let mut library = context.open_library()?;
let bur_matches = search_cached_bur(query)?; let official_matches = library.search(query)?;
if official_matches.is_empty() {
if official_matches.is_empty() { println!("Official repo: no matches for {query:?}");
println!("Official repo: no matches for {query:?}"); } else {
} else { println!("Official repo:");
println!("Official repo:"); for (name, score) in official_matches {
for (name, score) in official_matches { println!(" {} ({score:.2})", name);
println!(" {} ({score:.2})", name); }
} }
} }
if bur_matches.is_empty() { let bur_matches = search_cached_bur(query)?;
println!("Cached BUR: no matches for {query:?}"); if !bur_matches.is_empty() {
} else {
println!("Cached BUR:"); println!("Cached BUR:");
for entry in bur_matches { for entry in bur_matches {
if let Some(description) = &entry.description { if let Some(description) = &entry.description {
@@ -420,6 +440,23 @@ fn search_packages(context: &AppContext, query: &str) -> Result<(), Box<dyn std:
} }
} }
match AurClient::new().search(query, None) {
Ok(aur_packages) if !aur_packages.is_empty() => {
println!("AUR:");
for pkg in aur_packages {
let desc = if pkg.description.len() > 60 {
format!("{}...", &pkg.description[..57])
} else {
pkg.description.clone()
};
println!(" {}/{} ({}) [votes: {}]",
pkg.name, pkg.version, desc, pkg.num_votes);
}
}
Ok(_) => println!("AUR: no matches for {query:?}"),
Err(e) => eprintln!("AUR search failed: {e}"),
}
Ok(()) Ok(())
} }
@@ -467,6 +504,7 @@ fn sync_sources() -> Result<(), Box<dyn std::error::Error>> {
} }
fn system_upgrade(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> { fn system_upgrade(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> {
host_only_notice("system-upgrade")?;
sync_sources()?; sync_sources()?;
update_all(context) update_all(context)
} }
@@ -805,6 +843,7 @@ fn import_aur_target(target: &str) -> Result<(), Box<dyn std::error::Error>> {
} }
fn update_all(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> { fn update_all(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> {
host_only_notice("update-all")?;
let mut library = context.open_library()?; let mut library = context.open_library()?;
library.update(Vec::new())?; library.update(Vec::new())?;
let applied = apply_library_changes(&mut library)?; let applied = apply_library_changes(&mut library)?;
@@ -813,6 +852,7 @@ fn update_all(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> {
} }
fn remove_package(context: &AppContext, package: &str) -> Result<(), Box<dyn std::error::Error>> { fn remove_package(context: &AppContext, package: &str) -> Result<(), Box<dyn std::error::Error>> {
host_only_notice("remove")?;
let package_name = PackageName::new(package.to_string())?; let package_name = PackageName::new(package.to_string())?;
let mut library = context.open_library()?; let mut library = context.open_library()?;
library.uninstall(vec![package_name])?; library.uninstall(vec![package_name])?;
@@ -822,6 +862,7 @@ fn remove_package(context: &AppContext, package: &str) -> Result<(), Box<dyn std
} }
fn query_local_packages(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> { fn query_local_packages(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> {
host_only_notice("query")?;
let library = context.open_library()?; let library = context.open_library()?;
let mut packages = library.get_installed_packages()?; let mut packages = library.get_installed_packages()?;
packages.sort(); packages.sort();
@@ -839,6 +880,7 @@ fn query_local_packages(context: &AppContext) -> Result<(), Box<dyn std::error::
} }
fn query_local_info(context: &AppContext, package: &str) -> Result<(), Box<dyn std::error::Error>> { fn query_local_info(context: &AppContext, package: &str) -> Result<(), Box<dyn std::error::Error>> {
host_only_notice("query-info")?;
let mut library = context.open_library()?; let mut library = context.open_library()?;
let info = library.info(PackageName::new(package.to_string())?)?; let info = library.info(PackageName::new(package.to_string())?)?;
print_local_package_info(package, &info); print_local_package_info(package, &info);
@@ -849,6 +891,7 @@ fn query_local_files(
context: &AppContext, context: &AppContext,
package: &str, package: &str,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
host_only_notice("query-list")?;
let package_name = PackageName::new(package.to_string())?; let package_name = PackageName::new(package.to_string())?;
let library = context.open_library()?; let library = context.open_library()?;
let installed_packages = library.get_installed_packages()?; let installed_packages = library.get_installed_packages()?;
@@ -924,6 +967,20 @@ fn apply_library_changes(library: &mut Library) -> Result<usize, Box<dyn std::er
} }
} }
fn host_only_notice(command: &str) -> Result<(), Box<dyn std::error::Error>> {
if cfg!(not(target_os = "redox")) {
return Err(format!(
"'cubl {command}' requires Red Bear OS package database.\n\
On Linux, use:\n \
cubl search <query> — search AUR\n \
cubl -G <pkg> — fetch and convert AUR PKGBUILD\n \
cubl -B <dir> — cook recipe with host cross-compiler\n \
cubl -Si <pkg> — show AUR package info"
).into());
}
Ok(())
}
fn print_aur_package(package: &AurPackage) { fn print_aur_package(package: &AurPackage) {
println!("Repository : AUR"); println!("Repository : AUR");
println!("Name : {}", package.name); println!("Name : {}", package.name);