use std::borrow::Borrow;
use crate::action::conversions::WrongActionError;
use crate::action::ActionHashed;
use crate::action::CreateLink;
use crate::action::DeleteLink;
use crate::entry_def::EntryVisibility;
use crate::signature::Signature;
use crate::Action;
use crate::Entry;
use holo_hash::ActionHash;
use holo_hash::HashableContent;
use holo_hash::HoloHashOf;
use holo_hash::HoloHashed;
use holo_hash::PrimitiveHashType;
use holochain_serialized_bytes::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
#[cfg_attr(
feature = "fuzzing",
derive(arbitrary::Arbitrary, proptest_derive::Arbitrary)
)]
pub struct Record<A = SignedActionHashed> {
pub signed_action: A,
pub entry: RecordEntry<Entry>,
}
impl<A> AsRef<A> for Record<A> {
fn as_ref(&self) -> &A {
&self.signed_action
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, SerializedBytes)]
#[cfg_attr(
feature = "fuzzing",
derive(arbitrary::Arbitrary, proptest_derive::Arbitrary)
)]
pub enum RecordEntry<E: Borrow<Entry> = Entry> {
Present(E),
Hidden,
NA,
NotStored,
}
impl<E: Borrow<Entry>> From<E> for RecordEntry<E> {
fn from(entry: E) -> Self {
RecordEntry::Present(entry)
}
}
impl<E: Borrow<Entry>> RecordEntry<E> {
pub fn new(vis: Option<&EntryVisibility>, maybe_entry: Option<E>) -> Self {
match (maybe_entry, vis) {
(Some(entry), Some(_)) => RecordEntry::Present(entry),
(None, Some(EntryVisibility::Private)) => RecordEntry::Hidden,
(None, None) => RecordEntry::NA,
(Some(_), None) => {
unreachable!("Entry is present for an action type which has no entry reference")
}
(None, Some(EntryVisibility::Public)) => RecordEntry::NotStored,
}
}
pub fn as_option(&self) -> Option<&E> {
if let RecordEntry::Present(ref entry) = self {
Some(entry)
} else {
None
}
}
pub fn into_option(self) -> Option<E> {
if let RecordEntry::Present(entry) = self {
Some(entry)
} else {
None
}
}
pub fn to_app_option<A: TryFrom<SerializedBytes, Error = SerializedBytesError>>(
&self,
) -> Result<Option<A>, SerializedBytesError> {
match self.as_option().map(|e| e.borrow()) {
Some(Entry::App(eb)) => Ok(Some(A::try_from(SerializedBytes::from(eb.to_owned()))?)),
_ => Ok(None),
}
}
pub fn as_ref<'a>(&'a self) -> RecordEntry<&'a E>
where
&'a E: Borrow<Entry>,
{
match self {
RecordEntry::Present(ref e) => RecordEntry::Present(e),
RecordEntry::Hidden => RecordEntry::Hidden,
RecordEntry::NA => RecordEntry::NA,
RecordEntry::NotStored => RecordEntry::NotStored,
}
}
pub fn to_grant_option(&self) -> Option<crate::entry::CapGrantEntry> {
match self.as_option().map(|e| e.borrow()) {
Some(Entry::CapGrant(cap_grant_entry)) => Some(cap_grant_entry.to_owned()),
_ => None,
}
}
pub fn or_hidden(entry: Option<E>) -> Self {
entry.map(Self::Present).unwrap_or(Self::Hidden)
}
pub fn or_not_applicable(entry: Option<E>) -> Self {
entry.map(Self::Present).unwrap_or(Self::NA)
}
pub fn or_not_stored(entry: Option<E>) -> Self {
entry.map(Self::Present).unwrap_or(Self::NotStored)
}
}
pub type RecordEntryRef<'a> = RecordEntry<&'a Entry>;
pub type SignedActionHashed = SignedHashed<Action>;
impl AsRef<SignedActionHashed> for SignedActionHashed {
fn as_ref(&self) -> &SignedActionHashed {
self
}
}
#[derive(Clone, Debug, Eq, Serialize, Deserialize)]
pub struct SignedHashed<T>
where
T: HashableContent,
{
pub hashed: HoloHashed<T>,
pub signature: Signature,
}
impl Record {
pub fn new(signed_action: SignedActionHashed, maybe_entry: Option<Entry>) -> Self {
let maybe_visibility = signed_action.action().entry_visibility();
let entry = RecordEntry::new(maybe_visibility, maybe_entry);
Self {
signed_action,
entry,
}
}
pub fn signature(&self) -> &Signature {
self.signed_action.signature()
}
#[cfg(feature = "test_utils")]
pub fn as_action_mut(&mut self) -> &mut Action {
&mut self.signed_action.hashed.content
}
pub fn privatized(self) -> (Self, Option<Entry>) {
let (entry, hidden) = if let Some(EntryVisibility::Private) = self
.signed_action
.action()
.entry_data()
.map(|(_, entry_type)| entry_type.visibility())
{
match self.entry {
RecordEntry::Present(entry) => (RecordEntry::Hidden, Some(entry)),
other => (other, None),
}
} else {
(self.entry, None)
};
let privatized = Self {
signed_action: self.signed_action,
entry,
};
(privatized, hidden)
}
pub fn action_address(&self) -> &ActionHash {
self.signed_action.action_address()
}
pub fn action(&self) -> &Action {
self.signed_action.action()
}
pub fn action_hashed(&self) -> &ActionHashed {
&self.signed_action.hashed
}
pub fn entry(&self) -> &RecordEntry {
&self.entry
}
}
impl<A> Record<A> {
#[cfg(feature = "test_utils")]
pub fn as_entry_mut(&mut self) -> &mut RecordEntry {
&mut self.entry
}
pub fn into_inner(self) -> (A, RecordEntry) {
(self.signed_action, self.entry)
}
pub fn signed_action(&self) -> &A {
&self.signed_action
}
}
#[cfg(feature = "hashing")]
impl<T> SignedHashed<T>
where
T: HashableContent,
<T as holo_hash::HashableContent>::HashType: holo_hash::hash_type::HashTypeSync,
{
pub fn new_unchecked(content: T, signature: Signature) -> Self {
let hashed = HoloHashed::from_content_sync(content);
Self { hashed, signature }
}
}
impl<T> std::hash::Hash for SignedHashed<T>
where
T: HashableContent,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.signature.hash(state);
self.as_hash().hash(state);
}
}
impl<T> std::cmp::PartialEq for SignedHashed<T>
where
T: HashableContent,
{
fn eq(&self, other: &Self) -> bool {
self.hashed == other.hashed && self.signature == other.signature
}
}
impl<T> SignedHashed<T>
where
T: HashableContent,
{
pub fn into_inner(self) -> (HoloHashed<T>, Signature) {
(self.hashed, self.signature)
}
pub fn as_hash(&self) -> &HoloHashOf<T> {
&self.hashed.hash
}
pub fn with_presigned(hashed: HoloHashed<T>, signature: Signature) -> Self {
Self { hashed, signature }
}
pub fn signature(&self) -> &Signature {
&self.signature
}
}
impl SignedActionHashed {
pub fn action_address(&self) -> &ActionHash {
&self.hashed.hash
}
pub fn action(&self) -> &Action {
&self.hashed.content
}
pub fn raw_from_same_hash<T>(other: SignedHashed<T>) -> Self
where
T: Into<Action>,
T: HashableContent<HashType = holo_hash::hash_type::Action>,
{
let SignedHashed {
hashed: HoloHashed { content, hash },
signature,
} = other;
let action = content.into();
let hashed = ActionHashed::with_pre_hashed(action, hash);
Self { hashed, signature }
}
}
impl<C: HashableContent<HashType = T>, T: PrimitiveHashType> HashableContent for SignedHashed<C> {
type HashType = C::HashType;
fn hash_type(&self) -> Self::HashType {
T::new()
}
fn hashable_content(&self) -> holo_hash::HashableContentBytes {
use holo_hash::HasHash;
holo_hash::HashableContentBytes::Prehashed39(self.hashed.as_hash().get_raw_39().to_vec())
}
}
impl<T> From<SignedHashed<T>> for HoloHashed<T>
where
T: HashableContent,
{
fn from(sh: SignedHashed<T>) -> HoloHashed<T> {
sh.hashed
}
}
impl From<ActionHashed> for Action {
fn from(action_hashed: ActionHashed) -> Action {
action_hashed.into_content()
}
}
impl From<SignedActionHashed> for Action {
fn from(signed_action_hashed: SignedActionHashed) -> Action {
ActionHashed::from(signed_action_hashed).into()
}
}
impl From<Record> for Option<Entry> {
fn from(e: Record) -> Self {
e.entry.into_option()
}
}
impl TryFrom<Record> for CreateLink {
type Error = WrongActionError;
fn try_from(value: Record) -> Result<Self, Self::Error> {
value
.into_inner()
.0
.into_inner()
.0
.into_content()
.try_into()
}
}
impl TryFrom<Record> for DeleteLink {
type Error = WrongActionError;
fn try_from(value: Record) -> Result<Self, Self::Error> {
value
.into_inner()
.0
.into_inner()
.0
.into_content()
.try_into()
}
}
#[cfg(feature = "fuzzing")]
impl<'a, T> arbitrary::Arbitrary<'a> for SignedHashed<T>
where
T: HashableContent,
T: arbitrary::Arbitrary<'a>,
<T as holo_hash::HashableContent>::HashType: holo_hash::PrimitiveHashType,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self {
hashed: HoloHashed::<T>::arbitrary(u)?,
signature: Signature::arbitrary(u)?,
})
}
}