diff --git a/src/registry/fetcher.rs b/src/registry/fetcher.rs index ad9a1d1..fa0656a 100644 --- a/src/registry/fetcher.rs +++ b/src/registry/fetcher.rs @@ -10,15 +10,14 @@ use crate::core::{ util::{download, get_platform}, }; -use super::{package::Package, storage::RepositoryPackages}; +use super::package::Package; pub struct RegistryFetcher; #[derive(Deserialize)] struct RepositoryResponse { - bin: Vec, - base: Vec, - pkg: Vec, + #[serde(flatten)] + collection: HashMap>, } impl RegistryFetcher { @@ -43,29 +42,30 @@ impl RegistryFetcher { let parsed: RepositoryResponse = serde_json::from_slice(&content).context("Failed to parse registry json")?; - let convert_to_hashmap = |packages: Vec| -> HashMap> { - let mut result = HashMap::new(); - for package in packages { - let variant = package - .download_url - .split('/') - .rev() - .nth(1) - .map(|v| v.to_owned()) - .filter(|v| v != ARCH && v != &platform && v != &platform.replace('-', "_")); - let package_entry = result - .entry(package.name.to_owned()) - .or_insert_with(Vec::new); - package_entry.push(Package { variant, ..package }); - } - result - }; - - let package_registry = RepositoryPackages { - bin: convert_to_hashmap(parsed.bin), - base: convert_to_hashmap(parsed.base), - pkg: convert_to_hashmap(parsed.pkg), - }; + let package_registry: HashMap>> = parsed + .collection + .iter() + .map(|(key, packages)| { + let package_map: HashMap> = + packages.iter().fold(HashMap::new(), |mut acc, package| { + acc.entry(package.name.clone()).or_default().push(Package { + variant: package + .download_url + .split('/') + .rev() + .nth(1) + .map(|v| v.to_owned()) + .filter(|v| { + v != ARCH && v != &platform && v != &platform.replace('-', "_") + }), + ..package.clone() + }); + acc + }); + + (key.clone(), package_map) + }) + .collect(); let content = rmp_serde::to_vec(&package_registry) .context("Failed to serialize package registry to MessagePack")?; diff --git a/src/registry/installed.rs b/src/registry/installed.rs index 79d47fa..1f32387 100644 --- a/src/registry/installed.rs +++ b/src/registry/installed.rs @@ -63,7 +63,7 @@ impl InstalledPackages { pub fn is_installed(&self, package: &ResolvedPackage) -> bool { self.packages.iter().any(|installed| { installed.repo_name == package.repo_name - && installed.collection == package.collection.to_string() + && installed.collection == package.collection && installed.name == package.package.full_name('-') }) } @@ -71,7 +71,7 @@ impl InstalledPackages { fn find_package_mut(&mut self, package: &ResolvedPackage) -> Option<&mut InstalledPackage> { self.packages.iter_mut().find(|installed| { installed.repo_name == package.repo_name - && installed.collection == package.collection.to_string() + && installed.collection == package.collection && installed.name == package.package.full_name('-') }) } @@ -79,7 +79,7 @@ impl InstalledPackages { pub fn find_package(&self, package: &ResolvedPackage) -> Option<&InstalledPackage> { self.packages.iter().find(|installed| { installed.repo_name == package.repo_name - && installed.collection == package.collection.to_string() + && installed.collection == package.collection && installed.name == package.package.full_name('-') }) } @@ -117,7 +117,7 @@ impl InstalledPackages { true => { self.packages.retain(|installed| { !(installed.repo_name == resolved_package.repo_name - && installed.collection == resolved_package.collection.to_string() + && installed.collection == resolved_package.collection && installed.name == resolved_package.package.full_name('-')) }); } diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 8acc638..0548ac7 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -6,9 +6,7 @@ use fetcher::RegistryFetcher; use futures::future::try_join_all; use installed::InstalledPackages; use loader::RegistryLoader; -use package::{ - image::PackageImage, parse_package_query, update::Updater, ResolvedPackage, Collection, -}; +use package::{image::PackageImage, parse_package_query, update::Updater, ResolvedPackage}; use serde::Deserialize; use storage::{PackageStorage, RepositoryPackages}; use tokio::{fs, sync::Mutex}; @@ -320,19 +318,6 @@ impl PackageRegistry { } pub async fn list(&self, collection: Option<&str>) -> Result<()> { - let collection = match collection { - Some(rp) => match rp.to_lowercase().as_str() { - "base" => Ok(Some(Collection::Base)), - "bin" => Ok(Some(Collection::Bin)), - "pkg" => Ok(Some(Collection::Pkg)), - _ => Err(anyhow::anyhow!( - "Invalid collection: {}", - rp.color(Color::BrightGreen) - )), - }, - None => Ok(None), - }?; - let packages = self.storage.list_packages(collection); for resolved_package in packages { let package = resolved_package.package.clone(); diff --git a/src/registry/package/appimage.rs b/src/registry/package/appimage.rs index bfef8fe..058f7b6 100644 --- a/src/registry/package/appimage.rs +++ b/src/registry/package/appimage.rs @@ -93,8 +93,8 @@ fn is_appimage(file: &mut BufReader) -> bool { } async fn create_symlink(from: &Path, to: &Path) -> Result<()> { - if to.exists() { - if to.read_link().is_ok() && !to.read_link()?.starts_with(&*PACKAGES_PATH) { + if to.is_symlink() { + if to.exists() && !to.read_link()?.starts_with(&*PACKAGES_PATH) { error!( "{} is not managed by soar", to.to_string_lossy().color(Color::Blue) @@ -109,8 +109,8 @@ async fn create_symlink(from: &Path, to: &Path) -> Result<()> { } async fn remove_link(path: &Path) -> Result<()> { - if path.exists() { - if path.read_link().is_ok() && !path.read_link()?.starts_with(&*PACKAGES_PATH) { + if path.is_symlink() { + if path.exists() && !path.read_link()?.starts_with(&*PACKAGES_PATH) { error!( "{} is not managed by soar", path.to_string_lossy().color(Color::Blue) diff --git a/src/registry/package/install.rs b/src/registry/package/install.rs index 66e987b..7b07f96 100644 --- a/src/registry/package/install.rs +++ b/src/registry/package/install.rs @@ -13,10 +13,7 @@ use crate::{ error, registry::{ installed::InstalledPackages, - package::{ - appimage::{check_user_ns, extract_appimage, setup_portable_dir}, - Collection, - }, + package::appimage::{check_user_ns, extract_appimage, setup_portable_dir}, }, warn, }; @@ -169,7 +166,8 @@ impl Installer { self.save_file().await?; self.symlink_bin(&installed_packages).await?; - if self.resolved_package.collection == Collection::Pkg { + // TODO: use magic bytes instead + if self.resolved_package.collection == "pkg" { extract_appimage(package, &self.install_path).await?; setup_portable_dir( &package.bin_name, @@ -201,7 +199,7 @@ impl Installer { ); } - if self.resolved_package.collection == Collection::Pkg { + if self.resolved_package.collection == "pkg" { check_user_ns().await; } diff --git a/src/registry/package/mod.rs b/src/registry/package/mod.rs index 932ffcd..59d8e73 100644 --- a/src/registry/package/mod.rs +++ b/src/registry/package/mod.rs @@ -5,7 +5,7 @@ mod remove; pub mod run; pub mod update; -use std::{fmt::Display, path::PathBuf, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use anyhow::Result; use install::Installer; @@ -13,13 +13,7 @@ use remove::Remover; use serde::{Deserialize, Serialize}; use tokio::sync::Mutex; -use crate::{ - core::{ - color::{Color, ColorExt}, - constant::PACKAGES_PATH, - }, - error, -}; +use crate::core::constant::PACKAGES_PATH; use super::installed::InstalledPackages; @@ -47,7 +41,7 @@ pub struct Package { #[derive(Default, Debug, Clone)] pub struct ResolvedPackage { pub repo_name: String, - pub collection: Collection, + pub collection: String, pub package: Package, } @@ -111,37 +105,13 @@ impl Package { pub struct PackageQuery { pub name: String, pub variant: Option, - pub collection: Option, -} - -#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)] -pub enum Collection { - #[default] - Bin, - Base, - Pkg, + pub collection: Option, } pub fn parse_package_query(query: &str) -> PackageQuery { let (base_query, collection) = query .rsplit_once('#') - .map(|(n, r)| { - ( - n.to_owned(), - match r.to_lowercase().as_str() { - "base" => Some(Collection::Base), - "bin" => Some(Collection::Bin), - "pkg" => Some(Collection::Pkg), - _ => { - error!( - "Invalid collection path provided for {}", - query.color(Color::Red) - ); - std::process::exit(-1); - } - }, - ) - }) + .map(|(n, r)| (n.to_owned(), (!r.is_empty()).then(|| r.to_lowercase()))) .unwrap_or((query.to_owned(), None)); let (name, variant) = base_query @@ -155,23 +125,3 @@ pub fn parse_package_query(query: &str) -> PackageQuery { collection, } } - -impl Display for Collection { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Collection::Bin => write!(f, "bin"), - Collection::Base => write!(f, "base"), - Collection::Pkg => write!(f, "pkg"), - } - } -} - -impl From for Collection { - fn from(value: String) -> Self { - match value.as_ref() { - "base" => Collection::Base, - "pkg" => Collection::Pkg, - _ => Collection::Bin, - } - } -} diff --git a/src/registry/storage.rs b/src/registry/storage.rs index 3d51f57..b424ebb 100644 --- a/src/registry/storage.rs +++ b/src/registry/storage.rs @@ -30,7 +30,7 @@ use crate::{ }; use super::{ - package::{run::Runner, Package, PackageQuery, Collection}, + package::{run::Runner, Package, PackageQuery}, select_package_variant, }; @@ -41,9 +41,8 @@ pub struct PackageStorage { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct RepositoryPackages { - pub bin: HashMap>, - pub base: HashMap>, - pub pkg: HashMap>, + #[serde(flatten)] + pub collection: HashMap>>, } impl PackageStorage { @@ -178,82 +177,60 @@ impl PackageStorage { Ok(()) } - pub fn list_packages(&self, collection: Option) -> Vec { + pub fn list_packages(&self, collection: Option<&str>) -> Vec { self.repository .iter() .flat_map(|(repo_name, repo_packages)| { - let package_iterators = match collection { - Some(ref path) => match path { - Collection::Bin => vec![(&repo_packages.bin, Collection::Bin)], - Collection::Base => vec![(&repo_packages.base, Collection::Base)], - Collection::Pkg => vec![(&repo_packages.pkg, Collection::Pkg)], - }, - None => vec![ - (&repo_packages.bin, Collection::Bin), - (&repo_packages.base, Collection::Base), - (&repo_packages.pkg, Collection::Pkg), - ], - }; - - package_iterators.into_iter().flat_map(move |(map, path)| { - map.iter().flat_map(move |(_, packages)| { - let value = path.clone(); - packages.iter().map(move |package| ResolvedPackage { - repo_name: repo_name.clone(), - collection: value.clone(), - package: package.clone(), + repo_packages + .collection + .iter() + .filter(|(key, _)| collection.is_none() || Some(key.as_str()) == collection) + .flat_map(|(key, collections)| { + collections.iter().flat_map(|(_, packages)| { + packages.iter().map(|package| ResolvedPackage { + repo_name: repo_name.clone(), + collection: key.clone(), + package: package.clone(), + }) }) }) - }) }) - .collect::>() + .collect() } pub fn get_packages(&self, query: &PackageQuery) -> Option> { let pkg_name = query.name.trim(); - - let mut resolved_packages = Vec::new(); - for (repo_name, packages) in &self.repository { - let package_iterators = query - .collection - .to_owned() - .map(|collection| match collection { - Collection::Bin => vec![(&packages.bin, Collection::Bin)], - Collection::Base => vec![(&packages.base, Collection::Base)], - Collection::Pkg => vec![(&packages.pkg, Collection::Pkg)], - }) - .unwrap_or_else(|| { - vec![ - (&packages.bin, Collection::Bin), - (&packages.base, Collection::Base), - (&packages.pkg, Collection::Pkg), - ] - }); - - let pkgs: Vec = package_iterators - .iter() - .filter_map(|(map, collection)| { - map.get(pkg_name).map(|p| { - p.iter() - .filter(|pkg| { - pkg.name == pkg_name + let resolved_packages: Vec = self + .repository + .iter() + .flat_map(|(repo_name, packages)| { + packages + .collection + .iter() + .filter(|(collection_key, _)| { + query.collection.is_none() + || Some(collection_key.as_str()) == query.collection.as_deref() + }) + .flat_map(|(collection_key, map)| { + map.get(pkg_name).into_iter().flat_map(|pkgs| { + pkgs.iter().filter_map(|pkg| { + if pkg.name == pkg_name && (query.variant.is_none() || pkg.variant.as_ref() == query.variant.as_ref()) + { + Some(ResolvedPackage { + repo_name: repo_name.to_owned(), + package: pkg.clone(), + collection: collection_key.clone(), + }) + } else { + None + } }) - .cloned() - .map(|p| ResolvedPackage { - repo_name: repo_name.to_owned(), - package: p, - collection: collection.to_owned(), - }) - .collect::>() + }) }) - }) - .flatten() - .collect(); - - resolved_packages.extend(pkgs); - } + }) + .collect(); if !resolved_packages.is_empty() { Some(resolved_packages) @@ -265,28 +242,13 @@ impl PackageStorage { pub async fn search(&self, query: &str) -> Vec { let query = parse_package_query(query); let pkg_name = query.name.trim(); + let mut resolved_packages: Vec<(u32, Package, String, String)> = Vec::new(); - let mut resolved_packages: Vec<(u32, Package, Collection, String)> = Vec::new(); for (repo_name, packages) in &self.repository { - let package_iterators = query - .collection - .to_owned() - .map(|collection| match collection { - Collection::Bin => vec![(&packages.bin, Collection::Bin)], - Collection::Base => vec![(&packages.base, Collection::Base)], - Collection::Pkg => vec![(&packages.pkg, Collection::Pkg)], - }) - .unwrap_or_else(|| { - vec![ - (&packages.bin, Collection::Bin), - (&packages.base, Collection::Base), - (&packages.pkg, Collection::Pkg), - ] - }); - let pkgs: Vec<(u32, Package, Collection, String)> = package_iterators - .iter() - .flat_map(|(map, collection)| { - map.iter().flat_map(|(_, packages)| { + for (collection_name, collection_packages) in &packages.collection { + let pkgs: Vec<(u32, Package, String, String)> = collection_packages + .iter() + .flat_map(|(_, packages)| { packages.iter().filter_map(|pkg| { let mut score = 0; if pkg.name == pkg_name { @@ -296,14 +258,13 @@ impl PackageStorage { } else { return None; } - if query.variant.is_none() || pkg.variant.as_ref() == query.variant.as_ref() { Some(( score, pkg.to_owned(), - collection.to_owned(), + collection_name.to_owned(), repo_name.to_owned(), )) } else { @@ -311,27 +272,21 @@ impl PackageStorage { } }) }) - }) - .collect(); - - resolved_packages.extend(pkgs); + .collect(); + resolved_packages.extend(pkgs); + } } resolved_packages.sort_by(|(a, _, _, _), (b, _, _, _)| b.cmp(a)); - - let pkgs: Vec = resolved_packages + resolved_packages .into_iter() .filter(|(score, _, _, _)| *score > 0) - .collect::>() - .into_iter() .map(|(_, pkg, collection, repo_name)| ResolvedPackage { repo_name, package: pkg, collection, }) - .collect(); - - pkgs + .collect() } pub async fn inspect(&self, package_name: &str) -> Result<()> { @@ -415,7 +370,7 @@ impl PackageStorage { let repo = &CONFIG.repositories[0]; let base_url = format!("{}/{}", repo.url, platform); - let collection = if resolved_pkg.collection == Collection::Base { + let collection = if resolved_pkg.collection == "base" { "/Baseutils" } else { ""