notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

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 }