#![allow(dead_code)]
use std::collections::HashMap;
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum Value
{
Null,
Integer(usize),
Float(f64),
Average
{
current: f64,
count: f64,
},
}
impl From<f64> for Value
{
fn from(value: f64) -> Self
{
Value::Float(value)
}
}
impl From<usize> for Value
{
fn from(value: usize) -> Self
{
Value::Integer(value)
}
}
impl std::ops::Add for Value
{
type Output = Value;
fn add(self, rhs: Self) -> Self::Output
{
match self
{
Value::Null => rhs,
Value::Integer(i) => match rhs
{
Value::Null => self,
Value::Integer(ir) => (i + ir).into(),
Value::Float(fr) => (i as f64 + fr).into(),
Value::Average { .. } => Value::Null,
},
Value::Float(f) => match rhs
{
Value::Null => self,
Value::Integer(ir) => (f + ir as f64).into(),
Value::Float(fr) => (f + fr).into(),
Value::Average { .. } => Value::Null,
},
Value::Average { .. } => Value::Null,
}
}
}
impl std::fmt::Display for Value
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
match self
{
Value::Null => 0.fmt(f),
Value::Integer(i) => i.fmt(f),
Value::Float(fl) => fl.fmt(f),
Value::Average { current, .. } => current.fmt(f),
}
}
}
impl PartialOrd for Value
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>
{
match self
{
Value::Null => match other
{
Value::Null => Some(std::cmp::Ordering::Equal),
Value::Integer(ir) => 0_usize.partial_cmp(ir),
Value::Float(fr) => 0.0_f64.partial_cmp(fr),
Value::Average { current, .. } => 0.0_f64.partial_cmp(current),
},
Value::Integer(i) => match other
{
Value::Null => i.partial_cmp(&0),
Value::Integer(ir) => i.partial_cmp(ir),
Value::Float(fr) => (*i as f64).partial_cmp(fr),
Value::Average { current, .. } => (*i as f64).partial_cmp(current),
},
Value::Float(f) => match other
{
Value::Null => f.partial_cmp(&0.0),
Value::Integer(ir) => f.partial_cmp(&(*ir as f64)),
Value::Float(fr) => f.partial_cmp(fr),
Value::Average { current, .. } => f.partial_cmp(current),
},
Value::Average { current, .. } => match other
{
Value::Null => current.partial_cmp(&0.0),
Value::Integer(ir) => current.partial_cmp(&(*ir as f64)),
Value::Float(fr) => current.partial_cmp(fr),
Value::Average {
current: rcurrent, ..
} => current.partial_cmp(rcurrent),
},
}
}
}
impl Value
{
fn incremented(&self) -> Value
{
match self
{
Value::Null => 1.into(),
Value::Integer(i) => (i + 1).into(),
Value::Float(f) => (f + 1.0).into(),
Value::Average { current, .. } => (current + 1.0).into(),
}
}
}
#[derive(Clone, Default)]
pub(crate) struct Statistics
{
name: String,
statistics: ccutils::futures::ArcMutex<HashMap<String, Value>>,
}
trait HashMapExtension
{
fn get_value(&self, key: impl Into<String>) -> Value;
}
impl HashMapExtension for HashMap<String, Value>
{
fn get_value(&self, key: impl Into<String>) -> Value
{
*self.get(&key.into()).unwrap_or(&Value::Null)
}
}
static STATISTICS: std::sync::LazyLock<std::sync::Mutex<HashMap<String, Statistics>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(HashMap::<String, Statistics>::new()));
impl Statistics
{
pub(crate) fn get(name: impl Into<String>) -> Statistics
{
let mut stats = STATISTICS.lock().unwrap();
let name = name.into();
match stats.get(&name)
{
Some(st) => st.clone(),
None =>
{
let st = Statistics {
name: name.clone(),
..Default::default()
};
stats.insert(name, st.clone());
st
}
}
}
pub(crate) fn increment(&self, key: impl Into<String>)
{
futures::executor::block_on(self.increment_async(key));
}
pub(crate) async fn increment_async(&self, key: impl Into<String>)
{
let mut statistics = self.statistics.lock().await;
let key = key.into();
let count = statistics.get_value(&key);
statistics.insert(key, count.incremented());
}
pub(crate) async fn set_async(&self, key: impl Into<String>, value: Value)
{
let mut statistics = self.statistics.lock().await;
statistics.insert(key.into(), value);
}
pub(crate) async fn update_average_async(&self, key: impl Into<String>, value: f64)
{
let mut statistics = self.statistics.lock().await;
let key = key.into();
let avg = match statistics.get_value(&key)
{
Value::Average { current, count } => Value::Average {
current: (current * count + value) / (count + 1.0),
count: count + 1.0,
},
_ => Value::Average {
current: value,
count: 1.0,
},
};
statistics.insert(key, avg);
}
pub(crate) async fn display_async(
&self,
key: impl Into<String>,
key_ref: impl Into<String>,
delta: Value,
keys: impl IntoIterator<Item = &str>,
)
{
let mut statistics = self.statistics.lock().await;
let key_ref = key_ref.into();
let value = statistics.get_value(key.into());
let value_ref = statistics.get_value(&key_ref);
if value > value_ref + delta
{
statistics.insert(key_ref, value);
let mut r = String::new();
for it in keys.into_iter()
{
r += format!("{} {} ", it, statistics.get_value(it)).as_str();
}
log::info!("{} {}", self.name, r);
}
}
}