mod usages;
use chrono::{Duration, Utc};
use serde::{Deserialize, Serialize};
use std::collections::{btree_map::Entry::Occupied, BTreeMap};
use thiserror::Error;
pub use usages::Usages;
#[derive(Error, Debug)]
pub enum UsageTrackerError {
#[error("RON file could not be loaded")]
FileLoadErrorRon(#[source] ron::Error),
#[error("object \"{name}\" is already tracked")]
ObjectAlreadyTracked { name: String },
#[error("object \"{name}\" has never been used")]
ObjectNeverUsed { name: String },
#[error("object \"{name}\" doesn't exist")]
ObjectNotTracked { name: String },
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct UsageInformation {
usage_information: BTreeMap<String, Usages>,
}
impl UsageInformation {
pub fn add(&mut self, name: &String) -> Result<(), UsageTrackerError> {
if self.usage_information.contains_key(name) {
return Err(UsageTrackerError::ObjectAlreadyTracked {
name: name.to_owned(),
});
}
self.usage_information
.insert(name.to_owned(), Usages::new());
Ok(())
}
pub fn clear(&mut self) {
self.usage_information.clear();
}
pub fn list(&self) -> Vec<&String> {
self.usage_information.keys().collect()
}
pub fn list_verbose(&self) -> &BTreeMap<String, Usages> {
&self.usage_information
}
#[deprecated(
since = "0.2",
note = "please only use this function if you have to load files from v0.1"
)]
pub fn load_usage_information_from_ron_file<R>(rdr: R) -> Result<Self, UsageTrackerError>
where
R: std::io::Read,
{
Ok(Self {
usage_information: ron::de::from_reader(rdr)
.or_else(|e| return Err(UsageTrackerError::FileLoadErrorRon(e)))?,
})
}
pub fn new() -> Self {
Self {
usage_information: BTreeMap::new(),
}
}
pub fn prune(
&mut self,
name: &String,
before: &Option<chrono::DateTime<chrono::Utc>>,
) -> Result<(), UsageTrackerError> {
if let Occupied(mut e) = self.usage_information.entry(name.to_owned()) {
let usages = e.get_mut();
if before.is_some() {
usages.prune(before.unwrap());
} else {
usages.clear();
}
return Ok(());
} else {
return Err(UsageTrackerError::ObjectNotTracked {
name: name.to_owned(),
});
}
}
pub fn record_use(&mut self, name: &String, add_if_new: bool) -> Result<(), UsageTrackerError> {
if !add_if_new && !self.usage_information.contains_key(name) {
return Err(UsageTrackerError::ObjectNotTracked {
name: name.to_owned(),
});
}
self.usage_information
.entry(name.to_owned())
.or_insert(Usages::new())
.record_usage();
Ok(())
}
pub fn remove(&mut self, name: &String) {
if self.usage_information.contains_key(name) {
self.usage_information.remove(name);
}
}
pub fn usage(&self, name: &String, time_frame: &Duration) -> Result<f64, UsageTrackerError> {
if !self.usage_information.contains_key(name) {
return Err(UsageTrackerError::ObjectNotTracked {
name: name.to_owned(),
});
}
let ui = &self.usage_information[name].list();
if ui.is_empty() {
return Err(UsageTrackerError::ObjectNeverUsed {
name: name.to_owned(),
});
}
let time_since_first_use = Utc::now() - ui[0];
let percentage_of_time_since_first_use =
time_frame.num_seconds() as f64 / time_since_first_use.num_seconds() as f64;
Ok(percentage_of_time_since_first_use * ui.len() as f64)
}
pub fn usages(&self, name: &String) -> Result<&Usages, UsageTrackerError> {
if !self.usage_information.contains_key(name) {
return Err(UsageTrackerError::ObjectNotTracked {
name: name.to_owned(),
});
}
Ok(&self.usage_information[name])
}
}