use crate::{
chain_header::ChainHeader, entry::Entry, error::HolochainError, link::link_data::LinkData,
};
use holochain_json_api::{error::JsonError, json::JsonString};
use holochain_persistence_api::cas::content::{Address, AddressableContent, Content};
use std::{
convert::{Into, TryFrom},
fmt,
hash::{Hash, Hasher},
};
impl AddressableContent for EntryAspect {
fn content(&self) -> Content {
self.to_owned().into()
}
fn try_from_content(content: &Content) -> Result<Self, JsonError> {
Self::try_from(content.to_owned())
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, DefaultJson, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum EntryAspect {
Content(Entry, ChainHeader),
Header(ChainHeader),
LinkAdd(LinkData, ChainHeader),
LinkRemove((LinkData, Vec<Address>), ChainHeader),
Update(Entry, ChainHeader),
Deletion(ChainHeader),
}
impl EntryAspect {
pub fn type_hint(&self) -> String {
match self {
EntryAspect::Content(_, _) => String::from("content"),
EntryAspect::Header(_) => String::from("header"),
EntryAspect::LinkAdd(_, _) => String::from("link_add"),
EntryAspect::LinkRemove(_, _) => String::from("link_remove"),
EntryAspect::Update(_, _) => String::from("update"),
EntryAspect::Deletion(_) => String::from("deletion"),
}
}
pub fn header(&self) -> &ChainHeader {
match self {
EntryAspect::Content(_, header) => header,
EntryAspect::Header(header) => header,
EntryAspect::LinkAdd(_, header) => header,
EntryAspect::LinkRemove(_, header) => header,
EntryAspect::Update(_, header) => header,
EntryAspect::Deletion(header) => header,
}
}
pub fn entry_address(&self) -> Result<Address, HolochainError> {
Ok(match self {
EntryAspect::Content(_, header) => match header.link_update_delete() {
Some(ref updated_entry) => updated_entry.clone(),
None => header.entry_address().clone(),
},
EntryAspect::LinkAdd(link_data, _) => link_data.link.base().clone(),
EntryAspect::LinkRemove((link_data, _), _) => link_data.link.base().clone(),
EntryAspect::Update(_, header) | EntryAspect::Deletion(header) => {
header.link_update_delete().ok_or_else(|| {
HolochainError::ErrorGeneric(format!(
"no link_update_delete on Update/Deletion entry header. Header: {:?}",
header
))
})?
}
EntryAspect::Header(header) => header.address(),
})
}
}
fn format_header(header: &ChainHeader) -> String {
format!(
"Header[type: {}, crud_link: {:?}]",
header.entry_type(),
header.link_update_delete()
)
}
impl fmt::Debug for EntryAspect {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
EntryAspect::Content(entry, header) => write!(
f,
"EntryAspect::Content({}, {})",
entry.address(),
format_header(header)
),
EntryAspect::Header(header) => {
write!(f, "EntryAspect::Header({})", format_header(header))
}
EntryAspect::LinkAdd(link_data, header) => write!(
f,
"EntryAspect::LinkAdd({} -> {} [tag: {}, type: {}], {})",
link_data.link.base(),
link_data.link.target(),
link_data.link.tag(),
link_data.link.link_type(),
format_header(header)
),
EntryAspect::LinkRemove((link_data, _), header) => write!(
f,
"EntryAspect::LinkRemove({} -> {} [tag: {}, type: {}], {})",
link_data.link.base(),
link_data.link.target(),
link_data.link.tag(),
link_data.link.link_type(),
format_header(header)
),
EntryAspect::Update(entry, header) => write!(
f,
"EntryAspect::Update({}, {})",
entry.address(),
format_header(header)
),
EntryAspect::Deletion(header) => {
write!(f, "EntryAspect::Deletion({})", format_header(header))
}
}
}
}
#[allow(clippy::derive_hash_xor_eq)]
impl Hash for EntryAspect {
fn hash<H: Hasher>(&self, state: &mut H) {
self.header().hash(state);
self.type_hint().hash(state);
}
}