1use serde::{Deserialize, Serialize};
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use crate::utils::now;
6use crate::Error;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub(crate) struct Meta {
11 pub(crate) resource: String,
13 pub(crate) resource_path: PathBuf,
15 pub(crate) meta_path: PathBuf,
17 pub(crate) etag: Option<String>,
19 pub(crate) expires: Option<f64>,
21 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 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 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}