use std::collections::hash_map::DefaultHasher;
use std::collections::{BTreeMap, HashMap};
use std::hash::{Hash, Hasher};
use chrono::{DateTime, Utc};
use ecow::{EcoString, eco_format};
use uuid::Uuid;
pub trait Anonymize {
fn anonymize(&self) -> Self;
}
impl<T: Anonymize> Anonymize for Option<T> {
fn anonymize(&self) -> Self {
self.as_ref().map(|t| t.anonymize())
}
}
impl<T: Anonymize> Anonymize for Vec<T> {
fn anonymize(&self) -> Self {
self.iter().map(|e| e.anonymize()).collect()
}
}
impl<T: Anonymize> Anonymize for Box<T> {
fn anonymize(&self) -> Self {
Box::new(self.as_ref().anonymize())
}
}
impl<K: Anonymize + Hash + Eq, V: Anonymize> Anonymize for HashMap<K, V> {
fn anonymize(&self) -> Self {
self.iter()
.map(|(k, v)| (k.anonymize(), v.anonymize()))
.collect()
}
}
impl<K: Anonymize + Eq + Ord, V: Anonymize> Anonymize for BTreeMap<K, V> {
fn anonymize(&self) -> Self {
self.iter()
.map(|(k, v)| (k.anonymize(), v.anonymize()))
.collect()
}
}
pub fn anonymize_collection_values<C, K, V>(collection: &C) -> C
where
for<'a> &'a C: IntoIterator<Item = (&'a K, &'a V)>,
C: FromIterator<(K, V)>,
K: Clone,
V: Anonymize,
{
collection
.into_iter()
.map(|(k, v)| (k.clone(), v.anonymize()))
.collect()
}
pub fn anonymize_collection_values_opt<C, K, V>(collection_opt: &Option<C>) -> Option<C>
where
for<'a> &'a C: IntoIterator<Item = (&'a K, &'a V)>,
C: FromIterator<(K, V)>,
K: Clone,
V: Anonymize,
{
collection_opt
.as_ref()
.map(|c| anonymize_collection_values::<C, K, V>(c))
}
impl Anonymize for String {
fn anonymize(&self) -> Self {
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
hasher.finish().to_string()
}
}
impl Anonymize for EcoString {
fn anonymize(&self) -> Self {
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
eco_format!("{}", hasher.finish())
}
}
impl Anonymize for Uuid {
fn anonymize(&self) -> Self {
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
Uuid::from_u128(u128::from(hasher.finish()))
}
}
impl Anonymize for usize {
fn anonymize(&self) -> Self {
let log10 = (*self as f32).log10().round() as u32;
if log10 > 4 {
let skip_digits = log10 - 4;
let coeff = 10usize.pow(skip_digits);
(*self / coeff) * coeff
} else {
*self
}
}
}
impl Anonymize for bool {
fn anonymize(&self) -> Self {
*self
}
}
impl Anonymize for DateTime<Utc> {
fn anonymize(&self) -> Self {
let coeff: f32 = rand::random();
*self + chrono::Duration::try_seconds(((coeff * 20.0) - 10.0) as i64).unwrap_or_default()
}
}
impl Anonymize for serde_json::Value {
fn anonymize(&self) -> Self {
match self {
serde_json::Value::Null => serde_json::Value::Null,
serde_json::Value::Bool(b) => serde_json::Value::Bool(b.anonymize()),
serde_json::Value::Number(n) => serde_json::Value::Number(n.clone()),
serde_json::Value::String(s) => serde_json::Value::String(s.anonymize()),
serde_json::Value::Array(a) => {
serde_json::Value::Array(a.iter().map(|v| v.anonymize()).collect())
}
serde_json::Value::Object(o) => serde_json::Value::Object(
o.iter()
.map(|(k, v)| (k.anonymize(), v.anonymize()))
.collect(),
),
}
}
}