use std::hash::{Hash, Hasher};
use indexmap::IndexMap;
pub type RestorationMap = IndexMap<RestorationData, RestorationData>;
#[derive(Clone, Debug, Default, PartialEq)]
pub enum RestorationData {
#[default]
Null,
Bool(bool),
Int(i64),
Double(f64),
String(String),
Uint8List(Vec<u8>),
Int32List(Vec<i32>),
Int64List(Vec<i64>),
Float64List(Vec<f64>),
List(Vec<RestorationData>),
Map(RestorationMap),
}
impl RestorationData {
pub fn is_null(&self) -> bool {
matches!(self, RestorationData::Null)
}
pub fn as_bool(&self) -> Option<bool> {
match self {
RestorationData::Bool(value) => Some(*value),
_ => None,
}
}
pub fn as_int(&self) -> Option<i64> {
match self {
RestorationData::Int(value) => Some(*value),
_ => None,
}
}
pub fn as_double(&self) -> Option<f64> {
match self {
RestorationData::Double(value) => Some(*value),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
RestorationData::String(value) => Some(value),
_ => None,
}
}
pub fn as_uint8_list(&self) -> Option<&[u8]> {
match self {
RestorationData::Uint8List(values) => Some(values),
_ => None,
}
}
pub fn as_int32_list(&self) -> Option<&[i32]> {
match self {
RestorationData::Int32List(values) => Some(values),
_ => None,
}
}
pub fn as_int64_list(&self) -> Option<&[i64]> {
match self {
RestorationData::Int64List(values) => Some(values),
_ => None,
}
}
pub fn as_float64_list(&self) -> Option<&[f64]> {
match self {
RestorationData::Float64List(values) => Some(values),
_ => None,
}
}
pub fn as_list(&self) -> Option<&[RestorationData]> {
match self {
RestorationData::List(values) => Some(values),
_ => None,
}
}
pub fn as_map(&self) -> Option<&RestorationMap> {
match self {
RestorationData::Map(map) => Some(map),
_ => None,
}
}
pub fn as_map_mut(&mut self) -> Option<&mut RestorationMap> {
match self {
RestorationData::Map(map) => Some(map),
_ => None,
}
}
pub fn into_map(self) -> Option<RestorationMap> {
match self {
RestorationData::Map(map) => Some(map),
_ => None,
}
}
}
fn hash_double<H: Hasher>(value: f64, state: &mut H) {
let normalized = if value == 0.0 { 0.0 } else { value };
normalized.to_bits().hash(state);
}
impl Eq for RestorationData {}
impl Hash for RestorationData {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
RestorationData::Null => {}
RestorationData::Bool(value) => value.hash(state),
RestorationData::Int(value) => value.hash(state),
RestorationData::Double(value) => hash_double(*value, state),
RestorationData::String(value) => value.hash(state),
RestorationData::Uint8List(values) => values.hash(state),
RestorationData::Int32List(values) => values.hash(state),
RestorationData::Int64List(values) => values.hash(state),
RestorationData::Float64List(values) => {
for value in values {
hash_double(*value, state);
}
}
RestorationData::List(values) => values.hash(state),
RestorationData::Map(map) => map.len().hash(state),
}
}
}
impl From<bool> for RestorationData {
fn from(value: bool) -> RestorationData {
RestorationData::Bool(value)
}
}
impl From<i64> for RestorationData {
fn from(value: i64) -> RestorationData {
RestorationData::Int(value)
}
}
impl From<f64> for RestorationData {
fn from(value: f64) -> RestorationData {
RestorationData::Double(value)
}
}
impl From<&str> for RestorationData {
fn from(value: &str) -> RestorationData {
RestorationData::String(value.to_string())
}
}
impl From<String> for RestorationData {
fn from(value: String) -> RestorationData {
RestorationData::String(value)
}
}
impl From<Vec<u8>> for RestorationData {
fn from(values: Vec<u8>) -> RestorationData {
RestorationData::Uint8List(values)
}
}
impl From<Vec<i32>> for RestorationData {
fn from(values: Vec<i32>) -> RestorationData {
RestorationData::Int32List(values)
}
}
impl From<Vec<i64>> for RestorationData {
fn from(values: Vec<i64>) -> RestorationData {
RestorationData::Int64List(values)
}
}
impl From<Vec<f64>> for RestorationData {
fn from(values: Vec<f64>) -> RestorationData {
RestorationData::Float64List(values)
}
}
impl From<Vec<RestorationData>> for RestorationData {
fn from(values: Vec<RestorationData>) -> RestorationData {
RestorationData::List(values)
}
}
impl From<RestorationMap> for RestorationData {
fn from(map: RestorationMap) -> RestorationData {
RestorationData::Map(map)
}
}
impl<T: Into<RestorationData>> From<Option<T>> for RestorationData {
fn from(value: Option<T>) -> RestorationData {
match value {
Some(value) => value.into(),
None => RestorationData::Null,
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct RestorationUpdate {
pub enabled: bool,
pub data: Option<RestorationMap>,
}
#[cfg(test)]
mod tests {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use super::{RestorationData, RestorationMap};
fn hash_of(value: &RestorationData) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
#[test]
fn zero_and_negative_zero_are_one_key() {
let zero = RestorationData::from(0.0);
let negative_zero = RestorationData::from(-0.0);
assert_eq!(zero, negative_zero);
assert_eq!(hash_of(&zero), hash_of(&negative_zero));
}
#[test]
fn a_map_keyed_by_value_finds_what_it_stored() {
let mut map = RestorationMap::new();
map.insert("value1".into(), 10i64.into());
map.insert(12i64.into(), vec![1.5f64, 2.5].into());
assert_eq!(
map.get(&RestorationData::from("value1")).unwrap().as_int(),
Some(10)
);
assert_eq!(
map.get(&RestorationData::from(12i64))
.unwrap()
.as_float64_list(),
Some([1.5, 2.5].as_slice())
);
assert!(map.get(&RestorationData::from("missing")).is_none());
}
#[test]
fn an_optional_value_becomes_null() {
assert!(RestorationData::from(None::<i64>).is_null());
assert_eq!(RestorationData::from(Some(3i64)).as_int(), Some(3));
}
}