From a4cf97587eac4309a1bef86e918700befe85a201 Mon Sep 17 00:00:00 2001 From: Nicola Guerrera Date: Sun, 14 May 2023 14:59:36 +0200 Subject: [PATCH] Reverted xdg.rs --- Cargo.lock | 265 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/xdg.rs | 276 ++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 445 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bac1510..be43f9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" + [[package]] name = "ahash" version = "0.7.6" @@ -34,6 +40,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "aliasable" version = "0.1.3" @@ -72,6 +87,23 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "async-trait" +version = "0.1.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async_once" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" + [[package]] name = "autocfg" version = "1.1.0" @@ -141,6 +173,44 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "cached" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5877db5d1af7fae60d06b5db9430b68056a69b3582a0be8e3691e87654aeb6" +dependencies = [ + "async-trait", + "async_once", + "cached_proc_macro", + "cached_proc_macro_types", + "futures", + "hashbrown 0.13.2", + "instant", + "lazy_static", + "once_cell", + "thiserror", + "tokio", +] + +[[package]] +name = "cached_proc_macro" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc" +dependencies = [ + "cached_proc_macro_types", + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" + [[package]] name = "cc" version = "1.0.79" @@ -459,6 +529,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "dlv-list" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "either" version = "1.8.1" @@ -533,6 +612,15 @@ dependencies = [ "color-maps", ] +[[package]] +name = "file-locker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91c8a37357ab5d51f1a8053900fe8374283449695c5418b66f45deece50705f" +dependencies = [ + "nix 0.25.1", +] + [[package]] name = "flagset" version = "0.4.3" @@ -618,6 +706,16 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "freedesktop_entry_parser" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" +dependencies = [ + "nom", + "thiserror", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -625,16 +723,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] -name = "futures-core" -version = "0.3.27" +name = "futures" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] [[package]] name = "getrandom" @@ -708,12 +855,27 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash 0.4.7", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "heck" version = "0.4.1" @@ -809,7 +971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] @@ -904,6 +1066,29 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +[[package]] +name = "linicon" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee8c5653188a809616c97296180a0547a61dba205bcdcbdd261dbd022a25fd9" +dependencies = [ + "file-locker", + "freedesktop_entry_parser", + "linicon-theme", + "memmap2", + "thiserror", +] + +[[package]] +name = "linicon-theme" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f8240c33bb08c5d8b8cdea87b683b05e61037aa76ff26bef40672cc6ecbb80" +dependencies = [ + "freedesktop_entry_parser", + "rust-ini", +] + [[package]] name = "link-cplusplus" version = "1.0.8" @@ -974,6 +1159,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.7.1" @@ -992,6 +1186,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.6.2" @@ -1040,6 +1240,20 @@ dependencies = [ "getrandom", ] +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "libc", + "memoffset 0.6.5", + "pin-utils", +] + [[package]] name = "nix" version = "0.26.2" @@ -1054,6 +1268,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1140,6 +1364,16 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "ordered-multimap" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" +dependencies = [ + "dlv-list", + "hashbrown 0.9.1", +] + [[package]] name = "os_str_bytes" version = "6.4.1" @@ -1332,6 +1566,7 @@ dependencies = [ name = "protostar" version = "0.4.0" dependencies = [ + "cached", "clap", "color-eyre", "directories", @@ -1340,9 +1575,11 @@ dependencies = [ "glam 0.22.0", "image", "lazy_static", + "linicon", "manifest-dir-macros", "mint", - "nix", + "nix 0.26.2", + "regex", "resvg", "rustc-hash", "serde", @@ -1488,6 +1725,8 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -1565,6 +1804,16 @@ dependencies = [ "xmlparser", ] +[[package]] +name = "rust-ini" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -2236,7 +2485,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "371436099f2980de56dc385b615696d3eabbdac9649a72b85f9d75f68474fa9c" dependencies = [ - "ahash", + "ahash 0.7.6", "byteorder", "lazy_static", "parking_lot 0.11.2", diff --git a/src/xdg.rs b/src/xdg.rs index 395a1f5..0ff4bf1 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -1,4 +1,5 @@ use color_eyre::eyre::Result; +use lazy_static::lazy_static; use linicon; use regex::Regex; use resvg::render; @@ -12,33 +13,90 @@ use std::ffi::OsString; use std::fs::create_dir_all; use std::fs::File; use std::io::{BufRead, BufReader, ErrorKind}; -use std::os::unix::fs::symlink; +use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Mutex; use std::{env, fs}; use walkdir::WalkDir; + +#[serde_as] +#[derive(Deserialize, Serialize)] +struct ImageCache { + path: PathBuf, + #[serde_as(as = "Vec<(_, _)>")] + pub map: HashMap, +} + +impl ImageCache { + fn new(path: PathBuf) -> Self { + if let Ok(mut file) = File::open(&path) { + let mut buf = vec![]; + if file.read_to_end(&mut buf).is_ok() { + if let Ok(cache) = serde_json::from_slice(&buf[..]) { + return cache; + } + } + } + + //There was no file, or the file failed to load, create a new World. + ImageCache { + path, + map: HashMap::new(), + } + } + + fn insert(&mut self, k: String, v: PathBuf) { + self.map.insert(k, v); + } + + fn save(&self) { + let mut f = File::create(&self.path).unwrap(); + let buf = serde_json::to_vec(&self).unwrap(); + f.write_all(&buf[..]).unwrap(); + } +} + +lazy_static! { + static ref IMAGE_CACHE: Mutex = Mutex::new(ImageCache::new( + get_image_cache_dir().join("imagechache.map") + )); +} + fn get_data_dirs() -> Vec { let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS").unwrap_or_default(); - // Append the applications directory to each data directory - let app_dirs = xdg_data_dirs + let xdg_data_dirs = xdg_data_dirs_str .split(":") - .map(|dir| Path::new(dir).join("applications")); + .filter_map(|dir| PathBuf::from_str(dir).ok()); - // Get the user's local applications directory - let local_app_dir = dirs::home_dir() - .unwrap() + let data_home = dirs::home_dir() + .unwrap_or(PathBuf::from_str("/usr/share/").expect( + "No XDG_DATA_DIR set, no HOME directory found and no /usr/share direcotry found", + )) .join(".local") - .join("share") - .join("applications"); + .join("share"); - let desktop_extension = OsString::from_str("desktop").unwrap(); - - // Get the list of directories to search - app_dirs - .chain(Some(local_app_dir)) + xdg_data_dirs + .chain([data_home].into_iter()) .filter(|dir| dir.exists() && dir.is_dir()) + .collect() +} + +fn get_app_dirs() -> Vec { + get_data_dirs() + .into_iter() + .map(|dir| dir.join("applications")) + .filter(|dir| dir.exists() && dir.is_dir()) + .collect() +} + +pub fn get_desktop_files() -> Vec { + let desktop_extension = OsString::from_str("desktop").unwrap(); + // Get the list of directories to search + let app_dirs = get_app_dirs(); + app_dirs + .into_iter() .flat_map(|dir| { // Follow symlinks and recursively search directories WalkDir::new(dir) @@ -79,6 +137,10 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { let mut command = None; let mut categories = Vec::new(); let mut icon = None; + let mut no_display = false; + let mut desktop_entry_found = false; + + let re = Regex::new(r"^\[([^\]]*)\]$").unwrap(); // Loop through each line of the file for line in reader.lines() { @@ -92,6 +154,14 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { continue; } + if let Some(captures) = re.captures(&line) { + let entry = captures.get(1).unwrap(); + desktop_entry_found = entry.as_str().contains("Desktop Entry"); + } + + if !desktop_entry_found { + continue; + } // Split the line into a key-value pair by looking for the first "=" character let parts = line.split_once('='); let (key, value) = match parts { @@ -111,6 +181,12 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { .collect() } "Icon" => icon = Some(value.to_string()), + "NoDisplay" => { + no_display = match value { + "true" => true, + _ => false, + } + } _ => (), // Ignore unknown keys } } @@ -122,6 +198,7 @@ pub fn parse_desktop_file(path: PathBuf) -> Result { command, categories, icon, + no_display, }) } @@ -153,77 +230,96 @@ pub struct DesktopFile { pub command: Option, pub categories: Vec, pub icon: Option, + pub no_display: bool, } impl DesktopFile { - pub fn get_raw_icons(&self) -> Vec { + pub fn get_raw_icons(&self) -> Vec { // Get the name of the icon from the DesktopFile struct let Some(icon_name) = self.icon.as_ref() else { return Vec::new(); }; let test_icon_path = self.path.join(Path::new(icon_name)); if test_icon_path.exists() { - return RawIconType::from_path(test_icon_path) - .map(|i| vec![i]) - .unwrap_or_default(); - } - - let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize(); - if cache_icon_path.is_ok() { - if let Some(icon) = Icon::from_path(cache_icon_path.unwrap(), 128) { + if let Some(icon) = Icon::from_path(test_icon_path, 128) { return vec![icon]; } } - // Get the current icon theme from the XDG_ICON_THEME environment variable, or use "hicolor" as the default theme if the variable is not defined - let icon_theme = env::var_os("XDG_ICON_THEME").unwrap_or("hicolor".into()); + if let Some(cache_icon_path) = IMAGE_CACHE.lock().unwrap().map.get(icon_name) { + if cache_icon_path.exists() { + if let Some(icon) = Icon::from_path(cache_icon_path.to_owned(), 128) { + return vec![icon]; + } + } + } - // Get the XDG_DATA_HOME and XDG_DATA_DIRS environment variables, and split the XDG_DATA_DIRS variable into a list of directories - let Some(xdg_data_dirs) = env::var_os("XDG_DATA_DIRS") else { return Vec::new(); }; - let Ok(binding) = xdg_data_dirs.into_string() else { return Vec::new(); }; - let xdg_data_dirs = binding.split(":").map(Path::new); + let mut icons_iter = linicon::lookup_icon(icon_name) + .use_fallback_themes(false) + .peekable(); - // Concatenate the XDG_DATA_HOME and XDG_DATA_DIRS directories with the default path for icon themes - xdg_data_dirs // XDG_DATA_DIRS directories - .flat_map(|dir| { - let icons_path = dir.join("icons").join(&icon_theme); - ICON_SIZES - .iter() - .map(|path| icons_path.join(path).join("apps")) - .collect::>() - }) - .filter_map(|dir| { - let dir = fs::read_dir(dir).ok()?; - Some( - dir.filter_map(|e| e.ok()) - .map(|file| file.path()) - .filter(|file| file.file_stem() == Some(&icon_name)), - ) - }) - .flatten() - .filter_map(RawIconType::from_path) - .collect() + if icons_iter.peek().is_none() { + //dbg!("No icons found in current theme"); + icons_iter = linicon::lookup_icon(icon_name).peekable(); + } + + let sized_png: Vec = icons_iter + .filter_map(|i| i.ok()) + .filter(|i| i.icon_type != linicon::IconType::XMP) //TODO: support XMP + .map(|i| Icon::from_path(i.path, i.max_size - 2).unwrap()) + .collect(); + sized_png } } -#[derive(Debug, PartialEq, Eq)] -pub enum RawIconType { - Png(PathBuf), - Svg(PathBuf), - Gltf(PathBuf), +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Icon { + pub icon_type: IconType, + pub path: PathBuf, + pub size: u16, } -impl RawIconType { - pub fn from_path(path: PathBuf) -> Option { - match path.extension().and_then(|ext| ext.to_str()) { - Some("png") => Some(RawIconType::Png(path)), - Some("svg") => Some(RawIconType::Svg(path)), - Some("glb") | Some("gltf") => Some(RawIconType::Gltf(path)), - _ => None, - } + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum IconType { + Png, + Svg, + Gltf, +} +impl Icon { + pub fn from_path(path: PathBuf, size: u16) -> Option { + let icon_type = match path.extension().and_then(|ext| ext.to_str()) { + Some("png") => IconType::Png, + Some("svg") => IconType::Svg, + Some("glb") | Some("gltf") => IconType::Gltf, + _ => return None, + }; + return Some(Icon { + icon_type, + path, + size, + }); } pub fn cached_process(self, size: u16) -> Result { - let new_path = - get_image_cache_dir().join(self.path.with_extension("").file_name().unwrap()); - if !new_path.exists() { - _ = symlink(self.path.clone(), new_path); + if !IMAGE_CACHE.lock().unwrap().map.contains_key( + &self + .path + .with_extension("") + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_owned(), + ) { + dbg!("Saving value in the DB"); + IMAGE_CACHE.lock().unwrap().insert( + self.path + .with_extension("") + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_owned(), + self.path.clone(), + ); + IMAGE_CACHE.lock().unwrap().save(); } match self.icon_type { IconType::Svg => Ok(Icon::from_path(get_png_from_svg(self.path, size)?, size).unwrap()), @@ -241,6 +337,7 @@ fn test_get_icon_path() { command: None, categories: vec![], icon: Some("krita".into()), + no_display: false, }; // Call the get_icon_path() function with a size argument and store the result @@ -248,12 +345,15 @@ fn test_get_icon_path() { dbg!(&icon_paths); // Assert that the get_icon_path() function returns the expected result - assert!(icon_paths.contains(&RawIconType::Png(PathBuf::from( - "/usr/share/icons/hicolor/16x16/apps/krita.png" - )))); + assert!(icon_paths.contains( + &Icon::from_path( + PathBuf::from("/usr/share/icons/hicolor/32x32/apps/krita.png"), + 32 + ) + .unwrap() + )); } -#[cached] pub fn get_image_cache_dir() -> PathBuf { let cache_dir; if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") { @@ -267,26 +367,26 @@ pub fn get_image_cache_dir() -> PathBuf { return image_cache_dir; } -pub fn render_svg_to_png( - cache_dir: impl AsRef, - svg_path: impl AsRef, - size: u32, -) -> Result { +pub fn get_png_from_svg(svg_path: impl AsRef, size: u16) -> Result { let svg_path = fs::canonicalize(svg_path)?; - let tree = Tree::from_data( - fs::read(svg_path.as_path())?.as_slice(), - &resvg::usvg::Options::default(), - ) - .map_err(|_| ErrorKind::InvalidData)?; - create_dir_all(cache_dir.as_ref())?; - let png_path = cache_dir - .as_ref() - .join(svg_path.file_name().unwrap()) - .with_extension("png"); - let mut pixmap = Pixmap::new(size, size).unwrap(); + let svg_data = fs::read(svg_path.as_path())?; + let tree = Tree::from_data(svg_data.as_slice(), &resvg::usvg::Options::default()) + .map_err(|_| ErrorKind::InvalidData)?; + + let png_path = get_image_cache_dir().join(format!( + "{}-{}.png", + svg_path.file_name().unwrap().to_str().unwrap(), + svg_data.len() + )); + + if png_path.exists() { + return Ok(png_path); + } + + let mut pixmap = Pixmap::new(size.into(), size.into()).unwrap(); render( &tree, - FitTo::Width(size), + FitTo::Width(size.into()), Transform::identity(), pixmap.as_mut(), ); @@ -310,7 +410,7 @@ fn test_render_svg_to_png() { fs::write(&svg_path, test_svg_data).unwrap(); // Call the function with the test input and output paths and a size of 200 - let png_path = render_svg_to_png(".", &svg_path, 200).unwrap(); + let png_path = get_png_from_svg(&svg_path, 200).unwrap(); dbg!(&png_path); // Check that the output file exists