149 lines
3.4 KiB
Rust
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
|
|
}
|