imgcache.rs (3578B)
1 use crate::Result; 2 use egui::TextureHandle; 3 use poll_promise::Promise; 4 5 use egui::ColorImage; 6 7 use std::collections::HashMap; 8 use std::fs::{create_dir_all, File}; 9 10 use hex::ToHex; 11 use sha2::Digest; 12 use std::path; 13 use std::path::PathBuf; 14 use tracing::warn; 15 16 pub type ImageCacheValue = Promise<Result<TextureHandle>>; 17 pub type ImageCacheMap = HashMap<String, ImageCacheValue>; 18 19 pub struct ImageCache { 20 pub cache_dir: path::PathBuf, 21 url_imgs: ImageCacheMap, 22 } 23 24 impl ImageCache { 25 pub fn new(cache_dir: path::PathBuf) -> Self { 26 Self { 27 cache_dir, 28 url_imgs: HashMap::new(), 29 } 30 } 31 32 pub fn rel_dir() -> &'static str { 33 "img" 34 } 35 36 /* 37 pub fn fetch(image: &str) -> Result<Image> { 38 let m_cached_promise = img_cache.map().get(image); 39 if m_cached_promise.is_none() { 40 let res = crate::images::fetch_img( 41 img_cache, 42 ui.ctx(), 43 &image, 44 ImageType::Content(width.round() as u32, height.round() as u32), 45 ); 46 img_cache.map_mut().insert(image.to_owned(), res); 47 } 48 } 49 */ 50 51 pub fn write(cache_dir: &path::Path, url: &str, data: ColorImage) -> Result<()> { 52 let file_path = cache_dir.join(Self::key(url)); 53 if let Some(p) = file_path.parent() { 54 create_dir_all(p)?; 55 } 56 let file = File::options() 57 .write(true) 58 .create(true) 59 .truncate(true) 60 .open(file_path)?; 61 let encoder = image::codecs::webp::WebPEncoder::new_lossless(file); 62 63 encoder.encode( 64 data.as_raw(), 65 data.size[0] as u32, 66 data.size[1] as u32, 67 image::ColorType::Rgba8.into(), 68 )?; 69 70 Ok(()) 71 } 72 73 pub fn key(url: &str) -> String { 74 let k: String = sha2::Sha256::digest(url.as_bytes()).encode_hex(); 75 PathBuf::from(&k[0..2]) 76 .join(&k[2..4]) 77 .join(k) 78 .to_string_lossy() 79 .to_string() 80 } 81 82 /// Migrate from base32 encoded url to sha256 url + sub-dir structure 83 pub fn migrate_v0(&self) -> Result<()> { 84 for file in std::fs::read_dir(&self.cache_dir)? { 85 let file = if let Ok(f) = file { 86 f 87 } else { 88 // not sure how this could fail, skip entry 89 continue; 90 }; 91 if !file.path().is_file() { 92 continue; 93 } 94 let old_filename = file.file_name().to_string_lossy().to_string(); 95 let old_url = if let Some(u) = 96 base32::decode(base32::Alphabet::Crockford, &old_filename) 97 .and_then(|s| String::from_utf8(s).ok()) 98 { 99 u 100 } else { 101 warn!("Invalid base32 filename: {}", &old_filename); 102 continue; 103 }; 104 let new_path = self.cache_dir.join(Self::key(&old_url)); 105 if let Some(p) = new_path.parent() { 106 create_dir_all(p)?; 107 } 108 109 if let Err(e) = std::fs::rename(file.path(), &new_path) { 110 warn!( 111 "Failed to migrate file from {} to {}: {:?}", 112 file.path().display(), 113 new_path.display(), 114 e 115 ); 116 } 117 } 118 Ok(()) 119 } 120 121 pub fn map(&self) -> &ImageCacheMap { 122 &self.url_imgs 123 } 124 125 pub fn map_mut(&mut self) -> &mut ImageCacheMap { 126 &mut self.url_imgs 127 } 128 }