Cleaned up dependecies

This commit is contained in:
nik012003
2023-02-25 00:39:15 +01:00
committed by Nova
parent 692fded271
commit 8595199dcf
6 changed files with 156 additions and 139 deletions

10
Cargo.lock generated
View File

@@ -695,15 +695,6 @@ dependencies = [
"ttf-parser", "ttf-parser",
] ]
[[package]]
name = "fork"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9788ce090af4bf8d6e8f43d3f7d12305c787456387bd2d88856fcda3aa1f0dca"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "freedesktop_entry_parser" name = "freedesktop_entry_parser"
version = "1.3.0" version = "1.3.0"
@@ -1545,7 +1536,6 @@ dependencies = [
"directories", "directories",
"dirs", "dirs",
"ez-pixmap", "ez-pixmap",
"fork",
"glam 0.22.0", "glam 0.22.0",
"image", "image",
"lazy_static", "lazy_static",

View File

@@ -10,7 +10,6 @@ color-eyre = "0.6.2"
directories = "4.0.1" directories = "4.0.1"
dirs = "4.0.0" dirs = "4.0.0"
ez-pixmap = "0.2.2" ez-pixmap = "0.2.2"
fork = "0.1.20"
glam = { version = "0.22.0", features = ["mint"] } glam = { version = "0.22.0", features = ["mint"] }
image = "0.24.5" image = "0.24.5"
lazy_static = "1.4.0" lazy_static = "1.4.0"

View File

@@ -8,7 +8,9 @@ use protostar::{
}; };
use stardust_xr_fusion::{ use stardust_xr_fusion::{
client::{Client, FrameInfo, RootHandler}, client::{Client, FrameInfo, RootHandler},
spatial::Spatial, drawable::{Text, TextStyle, Bounds, TextFit, Alignment}, core::values::Transform, core::values::Transform,
drawable::{Alignment, Bounds, Text, TextFit, TextStyle},
spatial::Spatial,
}; };
const APP_LIMIT: usize = 300; const APP_LIMIT: usize = 300;
@@ -39,7 +41,6 @@ struct AppGrid {
//style: TextStyle, //style: TextStyle,
} }
impl AppGrid { impl AppGrid {
fn new(client: &Client) -> Self { fn new(client: &Client) -> Self {
let apps = get_desktop_files() let apps = get_desktop_files()
.into_iter() .into_iter()
@@ -96,15 +97,15 @@ impl App {
}; };
let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?;
let text = Text::create( let text = Text::create(
protostar.content_parent(), protostar.content_parent(),
Transform::from_position_rotation( Transform::from_position_rotation(
[0.0, 0.0, APP_SIZE / 2.0], [0.0, 0.0, APP_SIZE / 2.0],
Quat::from_rotation_y(3.14), Quat::from_rotation_y(3.14),
), ),
desktop_file.name.as_deref().unwrap_or("Unknown"), desktop_file.name.as_deref().unwrap_or("Unknown"),
style, style,
) )
.unwrap(); .unwrap();
protostar protostar
.content_parent() .content_parent()
.set_position(None, position) .set_position(None, position)

View File

@@ -8,7 +8,9 @@ use protostar::{
}; };
use stardust_xr_molecules::fusion::{ use stardust_xr_molecules::fusion::{
client::{Client, FrameInfo, RootHandler}, client::{Client, FrameInfo, RootHandler},
spatial::Spatial, drawable::{Text, TextStyle, Bounds, TextFit, Alignment}, core::values::Transform, core::values::Transform,
drawable::{Alignment, Bounds, Text, TextFit, TextStyle},
spatial::Spatial,
}; };
use tween::TweenTime; use tween::TweenTime;
@@ -20,32 +22,37 @@ struct Hex {
s: isize, s: isize,
} }
const HEX_CENTER: Hex = Hex{q:0,r:0,s:0}; const HEX_CENTER: Hex = Hex { q: 0, r: 0, s: 0 };
const HEX_DIRECTION_VECTORS: [Hex; 6] = [ const HEX_DIRECTION_VECTORS: [Hex; 6] = [
Hex{q:1, r:0, s:-1}, Hex{q:1, r:-1, s:0}, Hex{q:0, r:-1, s:1}, Hex { q: 1, r: 0, s: -1 },
Hex{q:-1, r:0, s:1}, Hex{q:-1, r:1, s:0}, Hex{q:0, r:1, s:-1}, Hex { q: 1, r: -1, s: 0 },
Hex { q: 0, r: -1, s: 1 },
Hex { q: -1, r: 0, s: 1 },
Hex { q: -1, r: 1, s: 0 },
Hex { q: 0, r: 1, s: -1 },
]; ];
impl Hex { impl Hex {
fn new(q:isize, r:isize, s:isize) -> Self{ fn new(q: isize, r: isize, s: isize) -> Self {
Hex{q:q, r:r, s:s} Hex { q: q, r: r, s: s }
} }
fn get_coords(&self) -> [f32; 3]{ fn get_coords(&self) -> [f32; 3] {
let x = 3.0/2.0 * APP_SIZE/2.0 * (-self.q-self.s).to_f32(); let x = 3.0 / 2.0 * APP_SIZE / 2.0 * (-self.q - self.s).to_f32();
let y = 3.0_f32.sqrt() * APP_SIZE/2.0 * ( (-self.q-self.s).to_f32()/2.0 + self.s.to_f32()); let y =
[x,y,0.0] 3.0_f32.sqrt() * APP_SIZE / 2.0 * ((-self.q - self.s).to_f32() / 2.0 + self.s.to_f32());
[x, y, 0.0]
} }
fn add(self, vec:&Hex) -> Self{ fn add(self, vec: &Hex) -> Self {
Hex::new(self.q + vec.q, self.r + vec.r, self.s + vec.s) Hex::new(self.q + vec.q, self.r + vec.r, self.s + vec.s)
} }
fn neighbor(self, direction:usize) -> Self{ fn neighbor(self, direction: usize) -> Self {
self.add(&HEX_DIRECTION_VECTORS[direction]) self.add(&HEX_DIRECTION_VECTORS[direction])
} }
fn scale(self, factor:isize) -> Self { fn scale(self, factor: isize) -> Self {
Hex::new(self.q * factor, self.r * factor, self.s * factor) Hex::new(self.q * factor, self.r * factor, self.s * factor)
} }
} }
@@ -73,7 +80,6 @@ struct AppHexGrid {
apps: Vec<App>, apps: Vec<App>,
} }
impl AppHexGrid { impl AppHexGrid {
fn new(client: &Client) -> Self { fn new(client: &Client) -> Self {
let mut desktop_files: Vec<DesktopFile> = get_desktop_files() let mut desktop_files: Vec<DesktopFile> = get_desktop_files()
.into_iter() .into_iter()
@@ -84,19 +90,30 @@ impl AppHexGrid {
desktop_files.sort_by_key(|d| d.clone().name.unwrap()); desktop_files.sort_by_key(|d| d.clone().name.unwrap());
let mut apps = Vec::new(); let mut apps = Vec::new();
let mut radius = 1; let mut radius = 1;
while !desktop_files.is_empty() { while !desktop_files.is_empty() {
let mut hex = HEX_CENTER.add(&HEX_DIRECTION_VECTORS[4].clone().scale(radius)); let mut hex = HEX_CENTER.add(&HEX_DIRECTION_VECTORS[4].clone().scale(radius));
for i in 0..6{ for i in 0..6 {
if desktop_files.is_empty() {break}; if desktop_files.is_empty() {
for _ in 0..radius{ break;
if desktop_files.is_empty() {break}; };
apps.push(App::new(client.get_root(),hex.get_coords(),desktop_files.pop().unwrap()).unwrap()); for _ in 0..radius {
hex = hex.neighbor(i); if desktop_files.is_empty() {
} break;
};
apps.push(
App::new(
client.get_root(),
hex.get_coords(),
desktop_files.pop().unwrap(),
)
.unwrap(),
);
hex = hex.neighbor(i);
}
} }
radius += 1; radius += 1;
} }
AppHexGrid { apps } AppHexGrid { apps }
} }
} }
@@ -120,7 +137,7 @@ impl App {
desktop_file: DesktopFile, desktop_file: DesktopFile,
) -> Option<Self> { ) -> Option<Self> {
let position = position.into(); let position = position.into();
let style= TextStyle { let style = TextStyle {
character_height: APP_SIZE * 0.1, character_height: APP_SIZE * 0.1,
bounds: Some(Bounds { bounds: Some(Bounds {
bounds: [APP_SIZE; 2].into(), bounds: [APP_SIZE; 2].into(),
@@ -132,15 +149,12 @@ impl App {
}; };
let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?;
let text = Text::create( let text = Text::create(
protostar.content_parent(), protostar.content_parent(),
Transform::from_position_rotation( Transform::from_position_rotation([0.0, 0.0, 0.004], Quat::from_rotation_y(3.14)),
[0.0, 0.0, 0.004], desktop_file.name.as_deref().unwrap_or("Unknown"),
Quat::from_rotation_y(3.14), style,
), )
desktop_file.name.as_deref().unwrap_or("Unknown"), .unwrap();
style,
)
.unwrap();
protostar protostar
.content_parent() .content_parent()
.set_position(None, position) .set_position(None, position)

View File

@@ -2,9 +2,7 @@ use crate::xdg::{DesktopFile, Icon, IconType};
use color_eyre::eyre::{eyre, Result}; use color_eyre::eyre::{eyre, Result};
use glam::Quat; use glam::Quat;
use mint::Vector3; use mint::Vector3;
use fork::{daemon, Fork, setsid}; use nix::unistd::setsid;
use std::process::{Command,Stdio};
use std::os::unix::process::CommandExt;
use stardust_xr_molecules::{ use stardust_xr_molecules::{
fusion::{ fusion::{
client::{Client, FrameInfo, RootHandler}, client::{Client, FrameInfo, RootHandler},
@@ -17,17 +15,18 @@ use stardust_xr_molecules::{
}, },
GrabData, Grabbable, GrabData, Grabbable,
}; };
use stardust_xr_molecules::{GrabData, Grabbable}; use std::os::unix::process::CommandExt;
use std::{f32::consts::PI, ffi::CStr, sync::Arc}; use std::process::{Command, Stdio};
use std::{f32::consts::PI, sync::Arc};
use tween::{QuartInOut, Tweener}; use tween::{QuartInOut, Tweener};
use ustr::ustr;
use nix::unistd::fork;
fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> { fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
return match &icon.icon_type { return match &icon.icon_type {
IconType::Png => { IconType::Png => {
let t = Transform::from_rotation_scale(Quat::from_rotation_x(PI/2.0)*Quat::from_rotation_y(PI),[0.03,0.03,0.03]); let t = Transform::from_rotation_scale(
Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI),
[0.03, 0.03, 0.03],
);
let model = Model::create( let model = Model::create(
parent, parent,
@@ -37,7 +36,7 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
model.set_material_parameter( model.set_material_parameter(
1, 1,
"color", "color",
MaterialParameter::Color([0.0,1.0,1.0,1.0]), MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]),
)?; )?;
model.set_material_parameter( model.set_material_parameter(
0, 0,
@@ -75,19 +74,16 @@ impl ProtoStar {
IconType::Gltf => true, IconType::Gltf => true,
_ => false, _ => false,
}) })
.or( .or(raw_icons.into_iter().max_by_key(|i| i.size));
raw_icons
.into_iter()
.max_by_key(|i| i.size)
);
match icon{ match icon {
Some(i) => { Some(i) => {
icon = match i.cached_process(128) { icon = match i.cached_process(128) {
Ok(i) => Some(i), Ok(i) => Some(i),
_ => None, _ => None,
}}, }
None => {}, }
None => {}
} }
Self::new_raw( Self::new_raw(
@@ -120,7 +116,10 @@ impl ProtoStar {
.unwrap_or_else(|| { .unwrap_or_else(|| {
Ok(Model::create( Ok(Model::create(
grabbable.content_parent(), grabbable.content_parent(),
Transform::from_rotation_scale(Quat::from_xyzw(0.0,0.707,0.707,0.0),[0.03,0.03,0.03]), Transform::from_rotation_scale(
Quat::from_xyzw(0.0, 0.707, 0.707, 0.0),
[0.03, 0.03, 0.03],
),
&ResourceID::new_namespaced("protostar", "hexagon/hexagon"), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"),
)?) )?)
})?; })?;
@@ -148,15 +147,15 @@ impl RootHandler for ProtoStar {
self.icon self.icon
.set_scale(None, Vector3::from([scale; 3])) .set_scale(None, Vector3::from([scale; 3]))
.unwrap(); .unwrap();
}
if let Some(icon_grow) = &mut self.icon_shrink {
if !icon_grow.is_finished(){
let scale = icon_grow.move_by(info.delta);
self.icon
.set_scale(None, Vector3::from([scale; 3]))
.unwrap();
} }
} if let Some(icon_grow) = &mut self.icon_shrink {
if !icon_grow.is_finished() {
let scale = icon_grow.move_by(info.delta);
self.icon
.set_scale(None, Vector3::from([scale; 3]))
.unwrap();
}
}
} else if self.grabbable.grab_action().actor_stopped() { } else if self.grabbable.grab_action().actor_stopped() {
let startup_settings = StartupSettings::create(&self.field.client().unwrap()).unwrap(); let startup_settings = StartupSettings::create(&self.field.client().unwrap()).unwrap();
self.icon self.icon
@@ -180,15 +179,15 @@ impl RootHandler for ProtoStar {
std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap());
unsafe { unsafe {
Command::new(executable) Command::new(executable)
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()) .stderr(Stdio::null())
.pre_exec(|| { .pre_exec(|| {
setsid(); setsid();
Ok(()) Ok(())
}) })
.spawn() .spawn()
.expect("Failed to start child process") .expect("Failed to start child process")
} }
}); });
self.icon_grow = Some(Tweener::quart_in_out(0.00, 0.03, 0.25)); //TODO make the scale a parameter self.icon_grow = Some(Tweener::quart_in_out(0.00, 0.03, 0.25)); //TODO make the scale a parameter

View File

@@ -1,29 +1,29 @@
use cached::proc_macro::cached;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use linicon;
use resvg::render; use resvg::render;
use resvg::tiny_skia::{Pixmap, Transform}; use resvg::tiny_skia::{Pixmap, Transform};
use resvg::usvg::{FitTo, Tree}; use resvg::usvg::{FitTo, Tree};
use std::ffi::OsString; use std::ffi::OsString;
use std::fs::{create_dir_all}; use std::fs::create_dir_all;
use std::os::unix::fs::{symlink};
use std::io::{BufRead, BufReader, ErrorKind}; use std::io::{BufRead, BufReader, ErrorKind};
use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::{env, fs}; use std::{env, fs};
use walkdir::WalkDir; use walkdir::WalkDir;
use linicon;
use cached::proc_macro::cached;
fn get_data_dirs() -> Vec<PathBuf> { fn get_data_dirs() -> Vec<PathBuf> {
let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS") let xdg_data_dirs_str = std::env::var("XDG_DATA_DIRS").unwrap_or_default();
.unwrap_or_default();
let xdg_data_dirs = xdg_data_dirs_str let xdg_data_dirs = xdg_data_dirs_str
.split(":") .split(":")
.filter_map(|dir| PathBuf::from_str(dir).ok()); .filter_map(|dir| PathBuf::from_str(dir).ok());
let data_home = dirs::home_dir() let data_home = dirs::home_dir()
.unwrap_or(PathBuf::from_str("/usr/share/") .unwrap_or(PathBuf::from_str("/usr/share/").expect(
.expect("No XDG_DATA_DIR set, no HOME directory found and no /usr/share direcotry found")) "No XDG_DATA_DIR set, no HOME directory found and no /usr/share direcotry found",
))
.join(".local") .join(".local")
.join("share"); .join("share");
@@ -33,7 +33,7 @@ fn get_data_dirs() -> Vec<PathBuf> {
.collect() .collect()
} }
fn get_app_dirs() -> Vec<PathBuf>{ fn get_app_dirs() -> Vec<PathBuf> {
get_data_dirs() get_data_dirs()
.into_iter() .into_iter()
.map(|dir| dir.join("applications")) .map(|dir| dir.join("applications"))
@@ -107,7 +107,7 @@ pub fn parse_desktop_file(path: PathBuf) -> Result<DesktopFile, String> {
Some((key, value)) => (key, value), Some((key, value)) => (key, value),
None => continue, None => continue,
}; };
// Parse the key-value pair based on the key // Parse the key-value pair based on the key
match key { match key {
"Name" => name = Some(value.to_string()), "Name" => name = Some(value.to_string()),
@@ -120,10 +120,12 @@ pub fn parse_desktop_file(path: PathBuf) -> Result<DesktopFile, String> {
.collect() .collect()
} }
"Icon" => icon = Some(value.to_string()), "Icon" => icon = Some(value.to_string()),
"NoDisplay" => no_display = match value{ "NoDisplay" => {
"true" => true, no_display = match value {
_ => false "true" => true,
}, _ => false,
}
}
_ => (), // Ignore unknown keys _ => (), // Ignore unknown keys
} }
} }
@@ -175,27 +177,29 @@ impl DesktopFile {
let Some(icon_name) = self.icon.as_ref() else { return Vec::new(); }; let Some(icon_name) = self.icon.as_ref() else { return Vec::new(); };
let test_icon_path = self.path.join(Path::new(icon_name)); let test_icon_path = self.path.join(Path::new(icon_name));
if test_icon_path.exists() { if test_icon_path.exists() {
if let Some(icon) = Icon::from_path(test_icon_path,128) { if let Some(icon) = Icon::from_path(test_icon_path, 128) {
return vec![icon] return vec![icon];
} }
} }
let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize(); let cache_icon_path = get_image_cache_dir().join(icon_name).canonicalize();
if cache_icon_path.is_ok() { if cache_icon_path.is_ok() {
return vec![Icon::from_path(cache_icon_path.unwrap(), 128).unwrap()] return vec![Icon::from_path(cache_icon_path.unwrap(), 128).unwrap()];
} }
let mut icons_iter= linicon::lookup_icon(icon_name).use_fallback_themes(false).peekable(); let mut icons_iter = linicon::lookup_icon(icon_name)
.use_fallback_themes(false)
if icons_iter.peek().is_none(){ .peekable();
if icons_iter.peek().is_none() {
//dbg!("No icons found in current theme"); //dbg!("No icons found in current theme");
icons_iter= linicon::lookup_icon(icon_name).peekable(); icons_iter = linicon::lookup_icon(icon_name).peekable();
} }
let sized_png : Vec<Icon> = icons_iter let sized_png: Vec<Icon> = icons_iter
.filter_map(|i| i.ok()) .filter_map(|i| i.ok())
.filter(|i| i.icon_type != linicon::IconType::XMP) //TODO: support XMP .filter(|i| i.icon_type != linicon::IconType::XMP) //TODO: support XMP
.map(|i| Icon::from_path(i.path,i.max_size - 2 ).unwrap()) .map(|i| Icon::from_path(i.path, i.max_size - 2).unwrap())
.collect(); .collect();
sized_png sized_png
} }
@@ -215,23 +219,29 @@ pub enum IconType {
Gltf, Gltf,
} }
impl Icon { impl Icon {
pub fn from_path(path: PathBuf, size: u16) -> Option<Icon>{ pub fn from_path(path: PathBuf, size: u16) -> Option<Icon> {
let icon_type = match path.extension().and_then(|ext| ext.to_str()) { let icon_type = match path.extension().and_then(|ext| ext.to_str()) {
Some("png") => Some(IconType::Png), Some("png") => Some(IconType::Png),
Some("svg") => Some(IconType::Svg), Some("svg") => Some(IconType::Svg),
Some("glb") | Some("gltf") => Some(IconType::Gltf), Some("glb") | Some("gltf") => Some(IconType::Gltf),
_ => {return None}, _ => return None,
}.unwrap(); }
return Some(Icon{icon_type,path,size}) .unwrap();
return Some(Icon {
icon_type,
path,
size,
});
} }
pub fn cached_process(self, size: u16) -> Result<Icon, std::io::Error> { pub fn cached_process(self, size: u16) -> Result<Icon, std::io::Error> {
let new_path = get_image_cache_dir().join(self.path.with_extension("").file_name().unwrap()); let new_path =
if !new_path.exists(){ get_image_cache_dir().join(self.path.with_extension("").file_name().unwrap());
if !new_path.exists() {
_ = symlink(self.path.clone(), new_path); _ = symlink(self.path.clone(), new_path);
} }
match self.icon_type { match self.icon_type {
IconType::Svg => Ok(Icon::from_path(get_png_from_svg(self.path, size)?,size).unwrap()), IconType::Svg => Ok(Icon::from_path(get_png_from_svg(self.path, size)?, size).unwrap()),
_ => Ok(self), _ => Ok(self),
} }
} }
@@ -254,39 +264,43 @@ fn test_get_icon_path() {
dbg!(&icon_paths); dbg!(&icon_paths);
// Assert that the get_icon_path() function returns the expected result // Assert that the get_icon_path() function returns the expected result
assert!(icon_paths.contains(&Icon::from_path(PathBuf::from("/usr/share/icons/hicolor/32x32/apps/krita.png"),32).unwrap())); assert!(icon_paths.contains(
&Icon::from_path(
PathBuf::from("/usr/share/icons/hicolor/32x32/apps/krita.png"),
32
)
.unwrap()
));
} }
#[cached] #[cached]
pub fn get_image_cache_dir() -> PathBuf { pub fn get_image_cache_dir() -> PathBuf {
let cache_dir; let cache_dir;
if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") { if let Ok(xdg_cache_home) = std::env::var("XDG_CACHE_HOME") {
cache_dir = PathBuf::from_str(&xdg_cache_home).unwrap_or( cache_dir =
dirs::home_dir().unwrap().join(".cache") PathBuf::from_str(&xdg_cache_home).unwrap_or(dirs::home_dir().unwrap().join(".cache"))
)
} else { } else {
cache_dir = dirs::home_dir().unwrap().join(".cache"); cache_dir = dirs::home_dir().unwrap().join(".cache");
} }
let image_cache_dir = cache_dir.join("protostar_icon_cache"); let image_cache_dir = cache_dir.join("protostar_icon_cache");
create_dir_all(&image_cache_dir).expect("Could not create image cache directory"); create_dir_all(&image_cache_dir).expect("Could not create image cache directory");
return image_cache_dir return image_cache_dir;
} }
pub fn get_png_from_svg(svg_path: impl AsRef<Path>, size: u16) -> Result<PathBuf, std::io::Error> {
pub fn get_png_from_svg(svg_path: impl AsRef<Path>, size: u16,) -> Result<PathBuf, std::io::Error> {
let svg_path = fs::canonicalize(svg_path)?; let svg_path = fs::canonicalize(svg_path)?;
let svg_data = fs::read(svg_path.as_path())?; let svg_data = fs::read(svg_path.as_path())?;
let tree = Tree::from_data( let tree = Tree::from_data(svg_data.as_slice(), &resvg::usvg::Options::default())
svg_data.as_slice(), .map_err(|_| ErrorKind::InvalidData)?;
&resvg::usvg::Options::default(),
) let png_path = get_image_cache_dir().join(format!(
.map_err(|_| ErrorKind::InvalidData)?; "{}-{}.png",
svg_path.file_name().unwrap().to_str().unwrap(),
let png_path = get_image_cache_dir() svg_data.len()
.join(format!("{}-{}.png",svg_path.file_name().unwrap().to_str().unwrap(), svg_data.len())); ));
if png_path.exists() { if png_path.exists() {
return Ok(png_path) return Ok(png_path);
} }
let mut pixmap = Pixmap::new(size.into(), size.into()).unwrap(); let mut pixmap = Pixmap::new(size.into(), size.into()).unwrap();