cached_path/
meta.rs

1use serde::{Deserialize, Serialize};
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use crate::utils::now;
6use crate::Error;
7
8/// Holds information about a cached resource.
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub(crate) struct Meta {
11    /// The original resource name.
12    pub(crate) resource: String,
13    /// Path to the cached resource.
14    pub(crate) resource_path: PathBuf,
15    /// Path to the serialized meta.
16    pub(crate) meta_path: PathBuf,
17    /// The ETAG of the resource from the time it was cached, if there was one.
18    pub(crate) etag: Option<String>,
19    /// Time that the freshness of this cached resource will expire.
20    pub(crate) expires: Option<f64>,
21    /// Time this version of the resource was cached.
22    pub(crate) creation_time: f64,
23}
24
25impl Meta {
26    pub(crate) fn new(
27        resource: String,
28        resource_path: PathBuf,
29        etag: Option<String>,
30        freshness_lifetime: Option<u64>,
31    ) -> Meta {
32        let mut expires: Option<f64> = None;
33        let creation_time = now();
34        if let Some(lifetime) = freshness_lifetime {
35            expires = Some(creation_time + (lifetime as f64));
36        }
37        let meta_path = Meta::meta_path(&resource_path);
38        Meta {
39            resource,
40            resource_path,
41            meta_path,
42            etag,
43            expires,
44            creation_time,
45        }
46    }
47
48    pub(crate) fn meta_path(resource_path: &Path) -> PathBuf {
49        let mut meta_path = PathBuf::from(resource_path);
50        let resource_file_name = meta_path.file_name().unwrap().to_str().unwrap();
51        let meta_file_name = format!("{resource_file_name}.meta");
52        meta_path.set_file_name(&meta_file_name[..]);
53        meta_path
54    }
55
56    pub(crate) fn get_extraction_path(&self) -> PathBuf {
57        let dirname = format!(
58            "{}-extracted",
59            self.resource_path.file_name().unwrap().to_str().unwrap()
60        );
61        self.resource_path.parent().unwrap().join(dirname)
62    }
63
64    pub(crate) fn to_file(&self) -> Result<(), Error> {
65        let serialized = serde_json::to_string(self).unwrap();
66        fs::write(&self.meta_path, &serialized[..])?;
67        Ok(())
68    }
69
70    pub(crate) fn from_cache(resource_path: &Path) -> Result<Self, Error> {
71        let meta_path = Meta::meta_path(resource_path);
72        Meta::from_path(&meta_path)
73    }
74
75    /// Read `Meta` from a path.
76    pub(crate) fn from_path(path: &Path) -> Result<Self, Error> {
77        if !path.is_file() {
78            return Err(Error::CacheCorrupted(format!("missing meta at {path:?}")));
79        }
80        let serialized = fs::read_to_string(path)?;
81        let meta: Meta = serde_json::from_str(&serialized[..])
82            .map_err(|e| Error::CacheCorrupted(format!("invalid meta at {path:?}: {e:?}")))?;
83        Ok(meta)
84    }
85
86    /// Check if resource is still fresh. Passing a `Some` value for
87    /// `freshness_lifetime` will override the expiration time (if there is one)
88    /// of this resource.
89    pub(crate) fn is_fresh(&self, freshness_lifetime: Option<u64>) -> bool {
90        if let Some(lifetime) = freshness_lifetime {
91            let expiration_time = self.creation_time + (lifetime as f64);
92            expiration_time > now()
93        } else if let Some(expiration_time) = self.expires {
94            expiration_time > now()
95        } else {
96            false
97        }
98    }
99}