feat: build Cub CLI and TUI workflows
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -13,6 +13,13 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
cub-lib = { path = "../cub-lib" }
|
||||
cub-tui = { path = "../cub-tui", optional = true }
|
||||
redox-pkg = { git = "https://gitlab.redox-os.org/redox-os/pkgutils.git", default-features = false, features = ["indicatif"] }
|
||||
clap = { workspace = true }
|
||||
pkgar = "0.2.2"
|
||||
pkgar-core = "0.2.2"
|
||||
termion = "4.0.6"
|
||||
|
||||
[features]
|
||||
default = ["tui"]
|
||||
tui = ["cub-tui"]
|
||||
|
||||
@@ -8,14 +8,20 @@ use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use cub::converter::{self, ConversionReport, ConversionResult};
|
||||
use clap::{CommandFactory, Parser, Subcommand};
|
||||
use cub::aur::{AurClient, AurPackage};
|
||||
use cub::cook;
|
||||
use cub::error::CubError;
|
||||
use cub::pkgbuild::{self, ConversionReport, ConversionResult};
|
||||
use cub::rbpkgbuild::RbPkgBuild;
|
||||
use cub::rbsrcinfo::RbSrcInfo;
|
||||
use cub::sandbox::SandboxConfig;
|
||||
use cub::storage::CubStore;
|
||||
use pkg::callback::IndicatifCallback;
|
||||
use pkg::{Library, PackageName};
|
||||
use pkg::{Library, PackageName, PackageState};
|
||||
use pkgar::ext::EntryExt;
|
||||
use pkgar::PackageFile;
|
||||
use pkgar_core::PackageSrc;
|
||||
|
||||
const DEFAULT_TARGET: &str = "x86_64-unknown-redox";
|
||||
const HOST_INSTALL_PATH: &str = "/tmp/pkg_install";
|
||||
@@ -26,6 +32,8 @@ const DEFAULT_BUR_REPO_URL: &str = "https://gitlab.redox-os.org/redox-os/bur.git
|
||||
const DEFAULT_AUR_BASE_URL: &str = "https://aur.archlinux.org";
|
||||
const PUBLIC_KEY_FILE: &str = "id_ed25519.pub.toml";
|
||||
const DEFAULT_SECRET_KEY_FILE: &str = "id_ed25519.toml";
|
||||
const PACKAGES_HEAD_DIR: &str = "var/lib/packages";
|
||||
const AUR_SYNC_STAMP_FILE: &str = "aur-sync.stamp";
|
||||
|
||||
struct CookbookAdapter;
|
||||
|
||||
@@ -42,7 +50,7 @@ struct PkgbuildConverter;
|
||||
|
||||
impl PkgbuildConverter {
|
||||
fn convert(content: &str) -> Result<ConversionResult, CubError> {
|
||||
converter::convert_pkgbuild(content)
|
||||
pkgbuild::convert_pkgbuild(content)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,10 +74,13 @@ impl PackageCreator {
|
||||
#[command(name = "cub")]
|
||||
#[command(version)]
|
||||
#[command(about = "Red Bear OS Package Builder")]
|
||||
#[command(arg_required_else_help = true)]
|
||||
struct Cli {
|
||||
/// Disable the default TUI launcher when no subcommand is provided
|
||||
#[arg(short = 'T', long = "no-tui", global = true)]
|
||||
no_tui: bool,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
command: Option<Commands>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
@@ -78,16 +89,32 @@ enum Commands {
|
||||
Install { package: String },
|
||||
/// Search packages in the official repo and cached BUR
|
||||
Search { query: String },
|
||||
/// Show AUR package details
|
||||
Info { package: String },
|
||||
/// Refresh cached package metadata
|
||||
Sync,
|
||||
/// Refresh metadata and update installed packages
|
||||
SystemUpgrade,
|
||||
/// Build and install a local RBPKGBUILD directory
|
||||
Build { dir: String },
|
||||
/// Fetch a BUR recipe into the current directory
|
||||
Get { package: String },
|
||||
/// Import an AUR package into ~/.cub/recipes
|
||||
GetAur { package: String },
|
||||
/// Inspect an installed package or local RBPKGBUILD
|
||||
Inspect { target: String },
|
||||
/// Convert an AUR PKGBUILD into an RBPKGBUILD tree
|
||||
ImportAur { target: String },
|
||||
/// Update all installed packages
|
||||
UpdateAll,
|
||||
/// Remove an installed package
|
||||
Remove { package: String },
|
||||
/// List installed packages
|
||||
QueryLocal,
|
||||
/// Show installed package details
|
||||
QueryInfo { package: String },
|
||||
/// List files installed by a package
|
||||
QueryList { package: String },
|
||||
/// Remove cub and pkg download caches
|
||||
CleanCache,
|
||||
}
|
||||
@@ -143,20 +170,63 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cli = Cli::parse_from(args);
|
||||
let context = AppContext::new();
|
||||
|
||||
match cli.command {
|
||||
Commands::Install { package } => install_package(&context, &package)?,
|
||||
Commands::Search { query } => search_packages(&context, &query)?,
|
||||
Commands::Build { dir } => build_local_dir(&context, Path::new(&dir))?,
|
||||
if let Some(command) = cli.command {
|
||||
run_command(&context, command)?;
|
||||
} else if cli.no_tui {
|
||||
print_help_text()?;
|
||||
} else {
|
||||
launch_tui_or_help()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_command(
|
||||
context: &AppContext,
|
||||
command: Commands,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
match command {
|
||||
Commands::Install { package } => install_package(context, &package)?,
|
||||
Commands::Search { query } => search_packages(context, &query)?,
|
||||
Commands::Info { package } => show_aur_info(&package)?,
|
||||
Commands::Sync => sync_sources()?,
|
||||
Commands::SystemUpgrade => system_upgrade(context)?,
|
||||
Commands::Build { dir } => build_local_dir(context, Path::new(&dir))?,
|
||||
Commands::Get { package } => fetch_bur_recipe(&package)?,
|
||||
Commands::Inspect { target } => inspect_target(&context, &target)?,
|
||||
Commands::GetAur { package } => get_aur_recipe(&package)?,
|
||||
Commands::Inspect { target } => inspect_target(context, &target)?,
|
||||
Commands::ImportAur { target } => import_aur_target(&target)?,
|
||||
Commands::UpdateAll => update_all(&context)?,
|
||||
Commands::UpdateAll => update_all(context)?,
|
||||
Commands::Remove { package } => remove_package(context, &package)?,
|
||||
Commands::QueryLocal => query_local_packages(context)?,
|
||||
Commands::QueryInfo { package } => query_local_info(context, &package)?,
|
||||
Commands::QueryList { package } => query_local_files(context, &package)?,
|
||||
Commands::CleanCache => clean_cache()?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn launch_tui_or_help() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[cfg(feature = "tui")]
|
||||
{
|
||||
cub_tui::run()?;
|
||||
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(
|
||||
args: impl IntoIterator<Item = OsString>,
|
||||
) -> Result<Vec<OsString>, Box<dyn std::error::Error>> {
|
||||
@@ -167,28 +237,77 @@ fn rewrite_shortcut_args(
|
||||
|
||||
let binary = collected[0].clone();
|
||||
let rest = &collected[1..];
|
||||
let Some(flag) = rest.first().and_then(|value| value.to_str()) else {
|
||||
let prefix_len = leading_global_flag_count(rest);
|
||||
let prefix = &rest[..prefix_len];
|
||||
let tail = &rest[prefix_len..];
|
||||
|
||||
let Some(flag) = tail.first().and_then(|value| value.to_str()) else {
|
||||
return Ok(collected);
|
||||
};
|
||||
|
||||
let rewrite_value = |subcommand: &str, value_name: &str| {
|
||||
if prefix.is_empty() {
|
||||
rewrite_value_command(binary.clone(), tail, subcommand, value_name)
|
||||
} else {
|
||||
rewrite_value_command_with_prefix(binary.clone(), prefix, tail, subcommand, value_name)
|
||||
}
|
||||
};
|
||||
let rewrite_flag = |subcommand: &str| {
|
||||
if prefix.is_empty() {
|
||||
rewrite_flag_command(binary.clone(), tail, subcommand)
|
||||
} else {
|
||||
rewrite_flag_command_with_prefix(binary.clone(), prefix, tail, subcommand)
|
||||
}
|
||||
};
|
||||
|
||||
match flag {
|
||||
"-S" => rewrite_value_command(binary, rest, "install", "package"),
|
||||
"-Ss" => rewrite_value_command(binary, rest, "search", "query"),
|
||||
"-B" => rewrite_value_command(binary, rest, "build", "dir"),
|
||||
"-G" => rewrite_value_command(binary, rest, "get", "package"),
|
||||
"-Pi" => rewrite_value_command(binary, rest, "inspect", "target"),
|
||||
"--import-aur" => rewrite_value_command(binary, rest, "import-aur", "target"),
|
||||
"-Sua" => rewrite_flag_command(binary, rest, "update-all"),
|
||||
"-Sc" => rewrite_flag_command(binary, rest, "clean-cache"),
|
||||
"-S" => rewrite_value("install", "package"),
|
||||
"-Ss" => rewrite_value("search", "query"),
|
||||
"-Si" => rewrite_value("info", "package"),
|
||||
"-Sy" => rewrite_flag("sync"),
|
||||
"-Syu" => rewrite_flag("system-upgrade"),
|
||||
"-B" => rewrite_value("build", "dir"),
|
||||
"-G" => rewrite_value("get-aur", "package"),
|
||||
"-R" => rewrite_value("remove", "package"),
|
||||
"-Q" => rewrite_flag("query-local"),
|
||||
"-Qi" => rewrite_value("query-info", "package"),
|
||||
"-Ql" => rewrite_value("query-list", "package"),
|
||||
"-Pi" => rewrite_value("inspect", "target"),
|
||||
"--import-aur" => rewrite_value("import-aur", "target"),
|
||||
"-Sua" => rewrite_flag("update-all"),
|
||||
"-Sc" => rewrite_flag("clean-cache"),
|
||||
_ => Ok(collected),
|
||||
}
|
||||
}
|
||||
|
||||
fn leading_global_flag_count(rest: &[OsString]) -> usize {
|
||||
let mut count = 0;
|
||||
while let Some(flag) = rest.get(count).and_then(|value| value.to_str()) {
|
||||
if matches!(flag, "-T" | "--no-tui") {
|
||||
count += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
fn rewrite_value_command(
|
||||
binary: OsString,
|
||||
rest: &[OsString],
|
||||
subcommand: &str,
|
||||
value_name: &str,
|
||||
) -> Result<Vec<OsString>, Box<dyn std::error::Error>> {
|
||||
rewrite_value_command_with_prefix(binary, &[], rest, subcommand, value_name)
|
||||
}
|
||||
|
||||
fn rewrite_value_command_with_prefix(
|
||||
binary: OsString,
|
||||
prefix: &[OsString],
|
||||
rest: &[OsString],
|
||||
subcommand: &str,
|
||||
value_name: &str,
|
||||
) -> Result<Vec<OsString>, Box<dyn std::error::Error>> {
|
||||
let Some(value) = rest.get(1) else {
|
||||
return Err(io::Error::new(
|
||||
@@ -198,7 +317,10 @@ fn rewrite_value_command(
|
||||
.into());
|
||||
};
|
||||
|
||||
let mut rewritten = vec![binary, OsString::from(subcommand), value.clone()];
|
||||
let mut rewritten = vec![binary];
|
||||
rewritten.extend(prefix.iter().cloned());
|
||||
rewritten.push(OsString::from(subcommand));
|
||||
rewritten.push(value.clone());
|
||||
rewritten.extend(rest.iter().skip(2).cloned());
|
||||
Ok(rewritten)
|
||||
}
|
||||
@@ -208,7 +330,18 @@ fn rewrite_flag_command(
|
||||
rest: &[OsString],
|
||||
subcommand: &str,
|
||||
) -> Result<Vec<OsString>, Box<dyn std::error::Error>> {
|
||||
let mut rewritten = vec![binary, OsString::from(subcommand)];
|
||||
rewrite_flag_command_with_prefix(binary, &[], rest, subcommand)
|
||||
}
|
||||
|
||||
fn rewrite_flag_command_with_prefix(
|
||||
binary: OsString,
|
||||
prefix: &[OsString],
|
||||
rest: &[OsString],
|
||||
subcommand: &str,
|
||||
) -> Result<Vec<OsString>, Box<dyn std::error::Error>> {
|
||||
let mut rewritten = vec![binary];
|
||||
rewritten.extend(prefix.iter().cloned());
|
||||
rewritten.push(OsString::from(subcommand));
|
||||
rewritten.extend(rest.iter().skip(1).cloned());
|
||||
Ok(rewritten)
|
||||
}
|
||||
@@ -274,7 +407,51 @@ fn search_packages(context: &AppContext, query: &str) -> Result<(), Box<dyn std:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_aur_info(package: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let client = AurClient::new();
|
||||
let packages = client.info(&[package])?;
|
||||
let aur_package = packages
|
||||
.into_iter()
|
||||
.find(|candidate| candidate.name == package)
|
||||
.ok_or_else(|| {
|
||||
CubError::PackageNotFound(format!("{package} not found in AUR info response"))
|
||||
})?;
|
||||
|
||||
print_aur_package(&aur_package);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sync_sources() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let bur_dir = sync_bur_repo()?;
|
||||
let store = init_cub_store()?;
|
||||
let aur_client = AurClient::new();
|
||||
let sample_results = aur_client.search("a", Some("name"))?;
|
||||
let stamp_path = store.sources_dir().join(AUR_SYNC_STAMP_FILE);
|
||||
fs::write(
|
||||
&stamp_path,
|
||||
format!(
|
||||
"synced_at_unix = {}\naur_sample_results = {}\n",
|
||||
current_unix_timestamp(),
|
||||
sample_results.len()
|
||||
),
|
||||
)?;
|
||||
|
||||
println!("Refreshed BUR cache at {}.", bur_dir.display());
|
||||
println!(
|
||||
"Verified live AUR metadata access and wrote sync stamp to {}.",
|
||||
stamp_path.display()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn system_upgrade(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> {
|
||||
sync_sources()?;
|
||||
update_all(context)
|
||||
}
|
||||
|
||||
fn build_local_dir(context: &AppContext, dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
cook::cook_available()?;
|
||||
|
||||
let rbpkg_path = dir.join("RBPKGBUILD");
|
||||
let rbpkg = RbPkgBuild::from_file(&rbpkg_path)?;
|
||||
rbpkg.validate()?;
|
||||
@@ -349,6 +526,60 @@ fn fetch_bur_recipe(package: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_aur_recipe(package: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let repo_url = aur_repo_url(package);
|
||||
let clone_dir = create_temp_dir("cub-aur-get")?;
|
||||
|
||||
let status = Command::new("git")
|
||||
.arg("clone")
|
||||
.arg("--depth")
|
||||
.arg("1")
|
||||
.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");
|
||||
let pkgbuild = fs::read_to_string(&pkgbuild_path)?;
|
||||
let conversion = PkgbuildConverter::convert(&pkgbuild)?;
|
||||
|
||||
let store = init_cub_store()?;
|
||||
let recipe_path = cub::recipe::save_recipe_to_store(&conversion.rbpkg, &store)?;
|
||||
let recipe_dir = recipe_path.parent().ok_or_else(|| {
|
||||
CubError::BuildFailed(format!("invalid saved recipe path {}", recipe_path.display()))
|
||||
})?;
|
||||
|
||||
fs::create_dir_all(recipe_dir.join("patches"))?;
|
||||
fs::create_dir_all(recipe_dir.join("import"))?;
|
||||
fs::write(recipe_dir.join("RBPKGBUILD"), conversion.rbpkg.to_toml()?)?;
|
||||
fs::write(
|
||||
recipe_dir.join(".RBSRCINFO"),
|
||||
RbSrcInfo::from_rbpkgbuild(&conversion.rbpkg).to_string(),
|
||||
)?;
|
||||
fs::write(recipe_dir.join("import").join("PKGBUILD"), pkgbuild)?;
|
||||
|
||||
let srcinfo_path = clone_dir.join(".SRCINFO");
|
||||
if srcinfo_path.is_file() {
|
||||
fs::copy(&srcinfo_path, recipe_dir.join("import").join(".SRCINFO"))?;
|
||||
}
|
||||
|
||||
let report = render_conversion_report(&conversion.report);
|
||||
fs::write(recipe_dir.join("import").join("report.txt"), &report)?;
|
||||
|
||||
println!(
|
||||
"Imported AUR package {} into {}.",
|
||||
conversion.rbpkg.package.name,
|
||||
recipe_dir.display()
|
||||
);
|
||||
println!("Saved cookbook recipe to {}.", recipe_path.display());
|
||||
println!("{report}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inspect_target(context: &AppContext, target: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let path = Path::new(target);
|
||||
if path.exists() {
|
||||
@@ -358,7 +589,7 @@ fn inspect_target(context: &AppContext, target: &str) -> Result<(), Box<dyn std:
|
||||
|
||||
let mut library = context.open_library()?;
|
||||
let info = library.info(PackageName::new(target.to_string())?)?;
|
||||
println!("{info:#?}");
|
||||
print_local_package_info(target, &info);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -411,9 +642,90 @@ fn update_all(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_package(context: &AppContext, package: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let package_name = PackageName::new(package.to_string())?;
|
||||
let mut library = context.open_library()?;
|
||||
library.uninstall(vec![package_name])?;
|
||||
let applied = apply_library_changes(&mut library)?;
|
||||
println!("Removed {} ({} change(s)).", package, applied);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query_local_packages(context: &AppContext) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let library = context.open_library()?;
|
||||
let mut packages = library.get_installed_packages()?;
|
||||
packages.sort();
|
||||
|
||||
if packages.is_empty() {
|
||||
println!("No packages are currently installed.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for package in packages {
|
||||
println!("{}", package);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query_local_info(
|
||||
context: &AppContext,
|
||||
package: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut library = context.open_library()?;
|
||||
let info = library.info(PackageName::new(package.to_string())?)?;
|
||||
print_local_package_info(package, &info);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query_local_files(
|
||||
context: &AppContext,
|
||||
package: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let package_name = PackageName::new(package.to_string())?;
|
||||
let package_state = PackageState::from_sysroot(&context.install_path)?;
|
||||
let install_state = package_state.installed.get(&package_name).ok_or_else(|| {
|
||||
CubError::PackageNotFound(format!("{package} is not installed under {}", context.install_path.display()))
|
||||
})?;
|
||||
let repo_key = package_state.pubkeys.get(&install_state.remote).ok_or_else(|| {
|
||||
CubError::BuildFailed(format!(
|
||||
"missing repository public key '{}' for installed package {}",
|
||||
install_state.remote, package
|
||||
))
|
||||
})?;
|
||||
|
||||
let head_path = context
|
||||
.install_path
|
||||
.join(PACKAGES_HEAD_DIR)
|
||||
.join(format!("{package}.pkgar_head"));
|
||||
let mut package_file = PackageFile::new(&head_path, &repo_key.pkey)?;
|
||||
|
||||
let mut paths = Vec::new();
|
||||
for entry in package_file.read_entries()? {
|
||||
let relative = entry.check_path()?;
|
||||
paths.push(format!("/{}", relative.display()));
|
||||
}
|
||||
paths.sort();
|
||||
|
||||
if paths.is_empty() {
|
||||
println!("{} has no recorded installed files.", package);
|
||||
} else {
|
||||
for path in paths {
|
||||
println!("{} {}", package, path);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clean_cache() -> Result<(), Box<dyn std::error::Error>> {
|
||||
remove_dir_if_exists(Path::new(PKG_DOWNLOAD_DIR))?;
|
||||
remove_dir_if_exists(Path::new(CUB_CACHE_DIR))?;
|
||||
if let Ok(store) = CubStore::new() {
|
||||
if let Err(error) = store.cache_clean() {
|
||||
eprintln!("Failed to clean Cub store sources cache: {error}");
|
||||
}
|
||||
}
|
||||
println!("Removed package caches from {PKG_DOWNLOAD_DIR} and {CUB_CACHE_DIR}.");
|
||||
Ok(())
|
||||
}
|
||||
@@ -430,6 +742,58 @@ fn apply_library_changes(library: &mut Library) -> Result<usize, Box<dyn std::er
|
||||
}
|
||||
}
|
||||
|
||||
fn print_aur_package(package: &AurPackage) {
|
||||
println!("Repository : AUR");
|
||||
println!("Name : {}", package.name);
|
||||
println!("Version : {}", package.version);
|
||||
println!("Description : {}", empty_if_blank(&package.description));
|
||||
println!("URL : {}", empty_if_blank(&package.url));
|
||||
println!("Licenses : {}", join_strings(&package.license));
|
||||
println!("Depends On : {}", join_strings(&package.depends));
|
||||
println!("Make Depends : {}", join_strings(&package.makedepends));
|
||||
println!("Optional Deps : {}", join_strings(&package.optdepends));
|
||||
println!("Provides : {}", join_strings(&package.provides));
|
||||
println!("Conflicts With : {}", join_strings(&package.conflicts));
|
||||
println!("Votes : {}", package.num_votes);
|
||||
println!("Popularity : {:.5}", package.popularity);
|
||||
println!("Last Modified : {}", package.last_modified);
|
||||
println!(
|
||||
"Out Of Date : {}",
|
||||
match package.out_of_date {
|
||||
Some(true) => "yes",
|
||||
Some(false) => "no",
|
||||
None => "unknown",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn print_local_package_info(package: &str, info: &pkg::PackageInfo) {
|
||||
println!("Repository : {}", info.package.remote);
|
||||
println!("Name : {}", info.package.package.name);
|
||||
println!("Version : {}", empty_if_blank(&info.package.package.version));
|
||||
println!("Target : {}", empty_if_blank(&info.package.package.target));
|
||||
println!("Installed : {}", yes_no(info.installed));
|
||||
println!("Depends On : {}", join_package_names(&info.package.package.depends));
|
||||
println!("Blake3 : {}", empty_if_blank(&info.package.package.blake3));
|
||||
println!("Storage Size : {}", info.package.package.storage_size);
|
||||
println!("Network Size : {}", info.package.package.network_size);
|
||||
println!(
|
||||
"Source ID : {}",
|
||||
empty_if_blank(&info.package.package.source_identifier)
|
||||
);
|
||||
println!(
|
||||
"Commit ID : {}",
|
||||
empty_if_blank(&info.package.package.commit_identifier)
|
||||
);
|
||||
println!(
|
||||
"Published At : {}",
|
||||
empty_if_blank(&info.package.package.time_identifier)
|
||||
);
|
||||
if info.package.package.name.as_str() != package {
|
||||
println!("Requested As : {}", package);
|
||||
}
|
||||
}
|
||||
|
||||
fn inspect_rbpkgbuild_path(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let rbpkg_path = if path.is_dir() {
|
||||
path.join("RBPKGBUILD")
|
||||
@@ -613,6 +977,12 @@ fn sync_bur_repo() -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
Ok(repo_dir)
|
||||
}
|
||||
|
||||
fn init_cub_store() -> Result<CubStore, Box<dyn std::error::Error>> {
|
||||
let store = CubStore::new()?;
|
||||
store.init()?;
|
||||
Ok(store)
|
||||
}
|
||||
|
||||
fn default_bur_repo_url() -> String {
|
||||
env::var("CUB_BUR_REPO_URL").unwrap_or_else(|_| DEFAULT_BUR_REPO_URL.to_string())
|
||||
}
|
||||
@@ -799,3 +1169,46 @@ fn remove_dir_if_exists(path: &Path) -> Result<(), io::Error> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn current_unix_timestamp() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_secs())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn join_strings(values: &[String]) -> String {
|
||||
if values.is_empty() {
|
||||
"None".to_string()
|
||||
} else {
|
||||
values.join(", ")
|
||||
}
|
||||
}
|
||||
|
||||
fn join_package_names(values: &[PackageName]) -> String {
|
||||
if values.is_empty() {
|
||||
"None".to_string()
|
||||
} else {
|
||||
values
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_if_blank(value: &str) -> &str {
|
||||
if value.trim().is_empty() {
|
||||
"None"
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fn yes_no(value: bool) -> &'static str {
|
||||
if value {
|
||||
"yes"
|
||||
} else {
|
||||
"no"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user