use crate::{Icon, IconKind, SiteIcons, CLIENT}; use cached::proc_macro::cached; use futures::future::join_all; use reqwest::IntoUrl; use serde::Deserialize; use std::error::Error; use url::Url; #[derive(Debug, Deserialize)] struct ManifestIcon { src: String, sizes: Option, } #[derive(Debug, Deserialize)] struct Manifest { icons: Vec, } impl SiteIcons { pub async fn load_manifest(url: U) -> Result, Box> { let url = url.into_url()?; Ok(load_manifest_cached(url).await?) } } #[cached(sync_writes = true)] async fn load_manifest_cached(url: Url) -> Result, String> { let url = &url; let manifest: Manifest = CLIENT .get(url.clone()) .send() .await .map_err(|e| format!("{}: {:?}", url, e))? .error_for_status() .map_err(|e| format!("{}: {:?}", url, e))? .json() .await .map_err(|e| format!("{}: {:?}", url, e))?; Ok( join_all(manifest.icons.into_iter().map(async move |icon| { if let Ok(src) = url.join(&icon.src) { Icon::load(src, IconKind::AppIcon, icon.sizes).await.ok() } else { None } })) .await .into_iter() .filter_map(|icon| icon) .collect(), ) }