diff -Nru cosmic-store-0.1.0~1705808532~20.04~7c04b72/debian/changelog cosmic-store-0.1.0~1705815540~20.04~151be8c/debian/changelog --- cosmic-store-0.1.0~1705808532~20.04~7c04b72/debian/changelog 2024-01-21 03:42:12.000000000 +0000 +++ cosmic-store-0.1.0~1705815540~20.04~151be8c/debian/changelog 2024-01-21 05:39:00.000000000 +0000 @@ -1,7 +1,7 @@ -cosmic-store (0.1.0~1705808532~20.04~7c04b72) focal; urgency=medium +cosmic-store (0.1.0~1705815540~20.04~151be8c) focal; urgency=medium * Auto Build * Initial release. - -- Pop OS (ISO Signing Key) Sat, 20 Jan 2024 20:42:12 -0700 + -- Pop OS (ISO Signing Key) Sat, 20 Jan 2024 22:39:00 -0700 diff -Nru cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/appstream_cache.rs cosmic-store-0.1.0~1705815540~20.04~151be8c/src/appstream_cache.rs --- cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/appstream_cache.rs 2024-01-21 03:42:12.000000000 +0000 +++ cosmic-store-0.1.0~1705815540~20.04~151be8c/src/appstream_cache.rs 2024-01-21 05:39:00.000000000 +0000 @@ -1,4 +1,4 @@ -use appstream::{Collection, Component}; +use appstream::{enums::Icon, Collection, Component}; use flate2::read::GzDecoder; use serde::Deserialize; use std::{ @@ -6,10 +6,13 @@ error::Error, fs, io::Read, - path::Path, + path::{Path, PathBuf}, time::SystemTime, }; +const PREFIXES: &'static [&'static str] = &["/usr/share", "/var/lib", "/var/cache"]; +const CATALOGS: &'static [&'static str] = &["swcatalog", "app-info"]; + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct AppstreamCacheTag { /// When the file was last modified in seconds from the unix epoch @@ -20,7 +23,7 @@ #[derive(Debug, Default)] pub struct AppstreamCache { - pub components: HashMap, + pub collections: HashMap, pub pkgnames: HashMap>, } @@ -30,13 +33,13 @@ // Uses btreemap for stable sort order let mut paths = BTreeMap::new(); //TODO: get using xdg dirs? - for prefix in &["/usr/share", "/var/lib", "/var/cache"] { + for prefix in PREFIXES { let prefix_path = Path::new(prefix); if !prefix_path.is_dir() { continue; } - for catalog in &["swcatalog", "app-info"] { + for catalog in CATALOGS { let catalog_path = prefix_path.join(catalog); if !catalog_path.is_dir() { continue; @@ -144,11 +147,11 @@ }; if file_name.ends_with(".xml.gz") { - println!("Compressed XML: {:?}", path); + log::info!("Compressed XML: {:?}", path); let mut gz = GzDecoder::new(&mut file); //TODO: support XML } else if file_name.ends_with(".yml.gz") { - println!("Compressed YAML: {:?}", path); + log::info!("Compressed YAML: {:?}", path); let mut gz = GzDecoder::new(&mut file); match appstream_cache.parse_yml(path, &mut gz) { Ok(()) => {} @@ -157,10 +160,10 @@ } } } else if file_name.ends_with(".xml") { - println!("XML: {:?}", path); + log::info!("XML: {:?}", path); //TODO: support XML } else if file_name.ends_with(".yml") { - println!("YAML: {:?}", path); + log::info!("YAML: {:?}", path); match appstream_cache.parse_yml(path, &mut file) { Ok(()) => {} Err(err) => { @@ -176,7 +179,53 @@ appstream_cache } + pub fn icon_path( + origin_opt: Option<&str>, + name: &Path, + width_opt: Option, + height_opt: Option, + scale_opt: Option, + ) -> Option { + //TODO: what to do with no origin? + let origin = origin_opt?; + //TODO: what to do with no width or height? + let width = width_opt?; + let height = height_opt?; + let size = match scale_opt { + //TODO: should a scale of 0 or 1 not add @scale? + Some(scale) => format!("{}x{}@{}", width, height, scale), + None => format!("{}x{}", width, height), + }; + + for prefix in PREFIXES { + let prefix_path = Path::new(prefix); + if !prefix_path.is_dir() { + continue; + } + + for catalog in CATALOGS { + let catalog_path = prefix_path.join(catalog); + if !catalog_path.is_dir() { + continue; + } + + let icon_path = catalog_path + .join("icons") + .join(origin) + .join(&size) + .join(name); + if icon_path.is_file() { + return Some(icon_path); + } + } + } + + None + } + fn parse_yml(&mut self, path: &Path, reader: R) -> Result<(), Box> { + let mut version_opt = None; + let mut origin_opt = None; for (doc_i, doc) in serde_yaml::Deserializer::from_reader(reader).enumerate() { let value = match serde_yaml::Value::deserialize(doc) { Ok(ok) => ok, @@ -186,18 +235,87 @@ } }; if doc_i == 0 { - //println!("HEADER: {:?}", value); + version_opt = value["Version"].as_str().map(|x| x.to_string()); + origin_opt = value["Origin"].as_str().map(|x| x.to_string()); } else { match Component::deserialize(&value) { Ok(mut component) => { - //TODO: full icon deserialize - match &value["Icon"]["stock"] { - serde_yaml::Value::String(icon_name) => { - component - .icons - .push(appstream::enums::Icon::Stock(icon_name.clone())); + //TODO: move to appstream crate + if let Some(icons) = value["Icon"].as_mapping() { + for (key, icon) in icons.iter() { + match key.as_str() { + Some("cached") => match icon.as_sequence() { + Some(sequence) => { + for cached in sequence { + match cached["name"].as_str() { + Some(name) => { + component.icons.push(Icon::Cached { + //TODO: add prefix? + path: PathBuf::from(name), + //TODO: handle parsing errors for these numbers + width: cached["width"] + .as_u64() + .and_then(|x| x.try_into().ok()), + height: cached["height"] + .as_u64() + .and_then(|x| x.try_into().ok()), + scale: cached["scale"] + .as_u64() + .and_then(|x| x.try_into().ok()), + }); + } + None => { + log::warn!( + "unsupported cached icon {:?} for {:?} in {:?}", + cached, + component.id, + path + ); + } + } + } + } + None => { + log::warn!( + "unsupported cached icons {:?} for {:?} in {:?}", + icon, + component.id, + path + ); + } + }, + Some("remote") => { + // For now we just ignore remote icons + log::debug!( + "ignoring remote icons {:?} for {:?} in {:?}", + icon, + component.id, + path + ); + } + Some("stock") => match icon.as_str() { + Some(stock) => { + component.icons.push(Icon::Stock(stock.to_string())); + } + None => { + log::warn!( + "unsupported stock icon {:?} for {:?} in {:?}", + icon, + component.id, + path + ); + } + }, + _ => { + log::warn!( + "unsupported icon kind {:?} for {:?} in {:?}", + key, + component.id, + path + ); + } + } } - _ => {} } let id = component.id.to_string(); @@ -207,10 +325,20 @@ .or_insert_with(|| HashSet::new()) .insert(id.clone()); } - match self.components.insert(id, component) { - Some(old_component) => { + match self.collections.insert( + id.clone(), + Collection { + //TODO: default version + version: version_opt.clone().unwrap_or_default(), + origin: origin_opt.clone(), + components: vec![component], + //TODO: architecture + architecture: None, + }, + ) { + Some(_old) => { //TODO: merge based on priority - log::warn!("found duplicate component {}", old_component.id); + log::debug!("found duplicate collection {}", id); } None => {} } diff -Nru cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/backend/flatpak.rs cosmic-store-0.1.0~1705815540~20.04~151be8c/src/backend/flatpak.rs --- cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/backend/flatpak.rs 2024-01-21 03:42:12.000000000 +0000 +++ cosmic-store-0.1.0~1705815540~20.04~151be8c/src/backend/flatpak.rs 2024-01-21 05:39:00.000000000 +0000 @@ -37,7 +37,7 @@ packages.push(Package { id: id.to_string(), //TODO: get icon from appstream data? - icon: widget::icon::from_name(id.to_string()), + icon: widget::icon::from_name(id.to_string()).size(128).handle(), name: r.appdata_name().unwrap_or(id).to_string(), version: r.appdata_version().unwrap_or_default().to_string(), extra, diff -Nru cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/backend/mod.rs cosmic-store-0.1.0~1705815540~20.04~151be8c/src/backend/mod.rs --- cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/backend/mod.rs 2024-01-21 03:42:12.000000000 +0000 +++ cosmic-store-0.1.0~1705815540~20.04~151be8c/src/backend/mod.rs 2024-01-21 05:39:00.000000000 +0000 @@ -13,7 +13,7 @@ #[derive(Clone, Debug)] pub struct Package { pub id: String, - pub icon: widget::icon::Named, + pub icon: widget::icon::Handle, pub name: String, pub version: String, pub extra: HashMap, diff -Nru cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/backend/packagekit.rs cosmic-store-0.1.0~1705815540~20.04~151be8c/src/backend/packagekit.rs --- cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/backend/packagekit.rs 2024-01-21 03:42:12.000000000 +0000 +++ cosmic-store-0.1.0~1705815540~20.04~151be8c/src/backend/packagekit.rs 2024-01-21 05:39:00.000000000 +0000 @@ -1,10 +1,10 @@ -use appstream::Collection; +use appstream::{enums::Icon, Collection}; use cosmic::widget; use packagekit_zbus::{ zbus::blocking::Connection, PackageKit::PackageKitProxyBlocking, Transaction::TransactionProxyBlocking, }; -use std::{collections::HashMap, error::Error, sync::Arc}; +use std::{cmp, collections::HashMap, error::Error, sync::Arc}; use super::{Backend, Package}; use crate::{get_translatable, AppstreamCache}; @@ -61,7 +61,7 @@ } else if member == "Finished" { break; } else { - println!("unknown signal {}", member); + log::warn!("unknown signal {}", member); } } None => {} @@ -81,40 +81,73 @@ let status_opt = data_parts.next(); let origin_opt = data_parts.next(); - let mut extra = HashMap::new(); - if let Some(architecture) = architecture_opt { - extra.insert("architecture".to_string(), architecture.to_string()); - } - if let Some(origin) = origin_opt { - extra.insert("origin".to_string(), origin.to_string()); - } match self.appstream_cache.pkgnames.get(package_name) { - Some(component_ids) => { - for component_id in component_ids.iter() { - match self.appstream_cache.components.get(component_id) { - Some(component) => { - let mut icon_name = "package-x-generic".to_string(); - for icon in component.icons.iter() { - //TODO: support other types of icons - match icon { - appstream::enums::Icon::Stock(stock) => { - icon_name = stock.clone(); + Some(ids) => { + for id in ids.iter() { + match self.appstream_cache.collections.get(id) { + Some(collection) => { + for component in collection.components.iter() { + let mut icon_opt = None; + let mut cached_size = 0; + for component_icon in component.icons.iter() { + //TODO: support other types of icons + match component_icon { + Icon::Cached { + path, + width, + height, + scale, + } => { + let size = cmp::min( + width.unwrap_or(0), + height.unwrap_or(0), + ); + if size < cached_size { + // Skip if size is less than cached size + continue; + } + if let Some(icon_path) = AppstreamCache::icon_path( + collection.origin.as_deref(), + path, + *width, + *height, + *scale, + ) { + icon_opt = + Some(widget::icon::from_path(icon_path)); + cached_size = size; + } + } + Icon::Stock(stock) => { + if cached_size != 0 { + // Skip if a cached icon was found + } + icon_opt = Some( + widget::icon::from_name(stock.clone()) + .size(128) + .handle(), + ); + } + _ => {} } - _ => {} } + packages.push(Package { + id: id.clone(), + //TODO: get icon from appstream data? + icon: icon_opt.unwrap_or_else(|| { + widget::icon::from_name("package-x-generic") + .size(128) + .handle() + }), + name: get_translatable(&component.name, &self.locale) + .to_string(), + version: version_opt.unwrap_or("").to_string(), + extra: HashMap::new(), + }); } - packages.push(Package { - id: component_id.clone(), - //TODO: get icon from appstream data? - icon: widget::icon::from_name(icon_name), - name: get_translatable(&component.name, &self.locale) - .to_string(), - version: version_opt.unwrap_or("").to_string(), - extra: extra.clone(), - }); } None => { - log::warn!("failed to find component {}", component_id); + log::warn!("failed to find component {}", id); } } } @@ -129,16 +162,8 @@ } fn appstream(&self, package: &Package) -> Result> { - match self.appstream_cache.components.get(&package.id) { - Some(component) => { - Ok(Collection { - //TODO: fill in this field - version: String::new(), - origin: package.extra.get("origin").cloned(), - components: vec![component.clone()], - architecture: package.extra.get("architecture").cloned(), - }) - } + match self.appstream_cache.collections.get(&package.id) { + Some(collection) => Ok(collection.clone()), None => Err(format!("failed to find component {}", package.id).into()), } } diff -Nru cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/main.rs cosmic-store-0.1.0~1705815540~20.04~151be8c/src/main.rs --- cosmic-store-0.1.0~1705808532~20.04~7c04b72/src/main.rs 2024-01-21 03:42:12.000000000 +0000 +++ cosmic-store-0.1.0~1705815540~20.04~151be8c/src/main.rs 2024-01-21 05:39:00.000000000 +0000 @@ -391,7 +391,7 @@ column = column.push(widget::button("Back").on_press(Message::SelectNone)); column = column.push( widget::row::with_children(vec![ - package.icon.clone().size(32).into(), + widget::icon::icon(package.icon.clone()).size(128).into(), widget::text(&package.name).into(), widget::horizontal_space(Length::Fill).into(), widget::text(&package.version).into(), @@ -428,7 +428,7 @@ column = column.push( widget::mouse_area( widget::row::with_children(vec![ - package.icon.clone().size(32).into(), + widget::icon::icon(package.icon.clone()).size(32).into(), widget::text(&package.name).into(), widget::horizontal_space(Length::Fill).into(), widget::text(&package.version).into(), Binary files /tmp/tmpdcbcu0z2/izmfCp7kcs/cosmic-store-0.1.0~1705808532~20.04~7c04b72/vendor.tar and /tmp/tmpdcbcu0z2/QWCrYVxLJT/cosmic-store-0.1.0~1705815540~20.04~151be8c/vendor.tar differ