use crate::addressing::{Address, Addressable}; use crate::database::inner::models; use crate::util::hash::{decode, hash, Hash, Hashable}; use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; use std::io::{Cursor, Write}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InEntry { pub entity: Option
, pub attribute: String, pub value: EntryValue, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Entry { pub entity: Address, pub attribute: String, pub value: EntryValue, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InvariantEntry { pub attribute: String, pub value: EntryValue, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(tag = "t", content = "c")] pub enum EntryValue { Value(serde_json::Value), Address(Address), Invalid, } impl TryFrom<&models::Entry> for Entry { type Error = anyhow::Error; fn try_from(e: &models::Entry) -> Result { Ok(Entry { entity: Address::decode(&e.entity)?, attribute: e.attribute.clone(), value: e.value.parse().unwrap(), }) } } impl TryFrom<&Entry> for models::Entry { type Error = anyhow::Error; fn try_from(e: &Entry) -> Result { Ok(models::Entry { identity: e.address()?.encode()?, entity: e.entity.encode()?, attribute: e.attribute.clone(), value: e.value.to_string()?, }) } } impl TryFrom<&InvariantEntry> for Entry { type Error = anyhow::Error; fn try_from(invariant: &InvariantEntry) -> Result { Ok(Entry { entity: invariant.entity()?, attribute: invariant.attribute.clone(), value: invariant.value.clone(), }) } } impl TryFrom for Entry { type Error = anyhow::Error; fn try_from(in_entry: InEntry) -> Result { match in_entry.entity { Some(entity) => Ok(Entry { entity, attribute: in_entry.attribute, value: in_entry.value, }), None => Ok(Entry::try_from(&InvariantEntry { attribute: in_entry.attribute, value: in_entry.value, })?), } } } impl InvariantEntry { pub fn entity(&self) -> Result
{ let mut entity = Cursor::new(vec![0u8; 0]); entity.write_all(self.attribute.as_bytes())?; entity.write_all(self.value.to_string()?.as_bytes())?; Ok(Address::Hash(hash(entity.into_inner()))) } } impl std::fmt::Display for Entry { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} | {} | {}", self.entity, self.attribute, self.value) } } impl Hashable for Entry { fn hash(self: &Entry) -> Result { let mut result = Cursor::new(vec![0u8; 0]); result.write_all(self.entity.encode()?.as_slice())?; result.write_all(self.attribute.as_bytes())?; result.write_all(self.value.to_string()?.as_bytes())?; Ok(hash(result.get_ref())) } } impl Hashable for InvariantEntry { fn hash(&self) -> Result { Entry::try_from(self)?.hash() } } impl Addressable for Entry {} impl Addressable for InvariantEntry {} impl EntryValue { pub fn to_string(&self) -> Result { let (type_char, content) = match self { EntryValue::Value(value) => ('J', serde_json::to_string(value)?), EntryValue::Address(address) => ('O', address.to_string()), EntryValue::Invalid => return Err(anyhow!("Cannot serialize invalid Entity value.")), }; Ok(format!("{}{}", type_char, content)) } } impl std::fmt::Display for EntryValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let (entry_type, entry_value) = match self { EntryValue::Address(address) => ("ADDRESS", address.to_string()), EntryValue::Value(value) => ( "VALUE", serde_json::to_string(value).unwrap_or_else(|_| String::from("?!?!?!")), ), EntryValue::Invalid => ("INVALID", "INVALID".to_string()), }; write!(f, "{}: {}", entry_type, entry_value) } } impl std::str::FromStr for EntryValue { type Err = std::convert::Infallible; fn from_str(s: &str) -> Result { if s.len() < 2 { Ok(EntryValue::Invalid) } else { let (type_char, content) = s.split_at(1); match (type_char, content) { ("J", content) => { if let Ok(value) = serde_json::from_str(content) { Ok(EntryValue::Value(value)) } else { Ok(EntryValue::Invalid) } } ("O", content) => { if let Ok(addr) = decode(content).and_then(|v| Address::decode(&v)) { Ok(EntryValue::Address(addr)) } else { Ok(EntryValue::Invalid) } } _ => Ok(EntryValue::Invalid), } } } }