1
0
Fork 0
mizule/src/cache.rs

149 lines
3.4 KiB
Rust

use chrono;
use chrono::Duration;
use serde::{Deserialize, Serialize};
use serde_json;
use std::collections::HashMap;
use std::env;
use std::fmt;
use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Debug)]
pub struct CacheEntry {
uuid: String,
changed: chrono::DateTime<chrono::Utc>,
}
impl CacheEntry {
pub fn changed(&self) -> chrono::DateTime<chrono::Utc> {
self.changed
}
pub fn expired(&self, ttl: Duration) -> bool {
let now = chrono::Utc::now();
now > self.changed + ttl
}
pub fn uuid(&self) -> &String {
return &self.uuid;
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Cache {
paths: HashMap<String, CacheEntry>,
device_id: Option<String>,
}
#[derive(Debug)]
pub enum Error {
Io(io::Error),
Json(serde_json::Error),
}
impl Error {
pub fn message(&self) -> String {
match *self {
Self::Io(ref e) => format!("{}", e),
Self::Json(ref e) => format!("{}", e),
}
}
}
impl From<io::Error> for Error {
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
Self::Json(error)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.message())
}
}
impl Cache {
pub fn new() -> Self {
Self {
paths: HashMap::new(),
device_id: None,
}
}
pub fn load(path: &str) -> Result<Self, Error> {
if let Ok(mut file) = fs::File::open(path) {
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let cache = serde_json::from_str(&contents);
match cache {
Ok(c) => Ok(c),
Err(e) => Err(e.into()),
}
} else {
Ok(Self::new())
}
}
pub fn get(&self, path: &str) -> Option<&CacheEntry> {
if let Some(entry) = self.paths.get(path) {
return Some(&entry);
}
None
}
pub fn update(&mut self, path: &str, uuid: &str) {
let entry = CacheEntry {
uuid: uuid.into(),
changed: chrono::Utc::now(),
};
self.paths.insert(path.into(), entry);
}
pub fn save(&self, path: &str) -> Result<(), Error> {
let path = PathBuf::from(path);
match path.parent() {
Some(p) => {
if !p.is_dir() {
fs::create_dir_all(p)?;
}
},
None => {},
};
let mut file = fs::File::create(path)?;
let contents = serde_json::to_string(&self)?;
file.write_all(&contents.as_bytes())?;
Ok(())
}
pub fn device_id(&self) -> Option<&str> {
match &self.device_id {
Some(p) => Some(p),
None => None,
}
}
pub fn set_device_id(&mut self, device_id: &str) {
self.device_id = Some(device_id.into());
}
}
pub fn get_cache_dir() -> PathBuf {
let mut dir = match env::var("XDG_CACHE_HOME") {
Ok(v) => PathBuf::from(&v),
Err(_) => match env::var("HOME") {
Ok(v) => [v, ".cache".into()].iter().collect(),
Err(_) => ".".into(),
},
};
dir.push(env!("CARGO_PKG_NAME"));
dir
}