upend/src/database/entry.rs

180 lines
5.2 KiB
Rust

use crate::addressing::{Address, Addressable};
use crate::hash::{decode, hash, Hash, Hashable};
use crate::models;
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<Address>,
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<Self, Self::Error> {
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<Self, Self::Error> {
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<Self, Self::Error> {
Ok(Entry {
entity: invariant.entity()?,
attribute: invariant.attribute.clone(),
value: invariant.value.clone(),
})
}
}
impl TryFrom<InEntry> for Entry {
type Error = anyhow::Error;
fn try_from(in_entry: InEntry) -> Result<Self, Self::Error> {
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<Address> {
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<Hash> {
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<Hash> {
Entry::try_from(self)?.hash()
}
}
impl Addressable for Entry {}
impl Addressable for InvariantEntry {}
impl EntryValue {
pub fn to_string(&self) -> Result<String> {
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<Self, Self::Err> {
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),
}
}
}
}