#![allow(missing_docs)]
use crate::prelude::*;
use crate::record::RecordStatus;
use crate::record::SignedActionHashedExt;
use conversions::WrongActionError;
use derive_more::From;
use holo_hash::EntryHash;
use holochain_zome_types::op::EntryCreationAction;
use holochain_zome_types::prelude::*;
#[cfg(feature = "contrafact")]
pub mod facts;
#[derive(
Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Hash, derive_more::From,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum NewEntryAction {
Create(Create),
Update(Update),
}
#[allow(missing_docs)]
#[derive(Debug, From)]
pub enum NewEntryActionRef<'a> {
Create(&'a Create),
Update(&'a Update),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Ord, PartialOrd)]
pub enum WireNewEntryAction {
Create(WireCreate),
Update(WireUpdate),
}
#[derive(
Debug, Clone, derive_more::Constructor, Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd,
)]
pub struct WireActionStatus<W> {
pub action: W,
pub validation_status: ValidationStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Ord, PartialOrd)]
pub struct WireCreate {
pub timestamp: holochain_zome_types::timestamp::Timestamp,
pub author: AgentPubKey,
pub action_seq: u32,
pub prev_action: ActionHash,
pub signature: Signature,
pub weight: EntryRateWeight,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes, Ord, PartialOrd)]
pub struct WireUpdate {
pub timestamp: holochain_zome_types::timestamp::Timestamp,
pub author: AgentPubKey,
pub action_seq: u32,
pub prev_action: ActionHash,
pub original_entry_address: EntryHash,
pub original_action_address: ActionHash,
pub signature: Signature,
pub weight: EntryRateWeight,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes)]
pub struct WireUpdateRelationship {
pub timestamp: holochain_zome_types::timestamp::Timestamp,
pub author: AgentPubKey,
pub action_seq: u32,
pub prev_action: ActionHash,
pub original_action_address: ActionHash,
pub new_entry_address: EntryHash,
pub new_entry_type: EntryType,
pub signature: Signature,
pub weight: EntryRateWeight,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SerializedBytes)]
pub struct WireDelete {
pub delete: Delete,
pub signature: Signature,
}
impl NewEntryAction {
pub fn entry(&self) -> &EntryHash {
match self {
NewEntryAction::Create(Create { entry_hash, .. })
| NewEntryAction::Update(Update { entry_hash, .. }) => entry_hash,
}
}
pub fn entry_type(&self) -> &EntryType {
match self {
NewEntryAction::Create(Create { entry_type, .. })
| NewEntryAction::Update(Update { entry_type, .. }) => entry_type,
}
}
pub fn visibility(&self) -> &EntryVisibility {
match self {
NewEntryAction::Create(Create { entry_type, .. })
| NewEntryAction::Update(Update { entry_type, .. }) => entry_type.visibility(),
}
}
pub fn timestamp(&self) -> holochain_zome_types::timestamp::Timestamp {
match self {
NewEntryAction::Create(Create { timestamp, .. })
| NewEntryAction::Update(Update { timestamp, .. }) => *timestamp,
}
}
}
impl From<NewEntryAction> for Action {
fn from(h: NewEntryAction) -> Self {
match h {
NewEntryAction::Create(h) => Action::Create(h),
NewEntryAction::Update(h) => Action::Update(h),
}
}
}
impl From<NewEntryAction> for EntryCreationAction {
fn from(action: NewEntryAction) -> Self {
match action {
NewEntryAction::Create(create) => EntryCreationAction::Create(create),
NewEntryAction::Update(update) => EntryCreationAction::Update(update),
}
}
}
impl From<(Create, Signature)> for WireCreate {
fn from((ec, signature): (Create, Signature)) -> Self {
Self {
timestamp: ec.timestamp,
author: ec.author,
action_seq: ec.action_seq,
prev_action: ec.prev_action,
signature,
weight: ec.weight,
}
}
}
impl From<(Update, Signature)> for WireUpdate {
fn from((eu, signature): (Update, Signature)) -> Self {
Self {
timestamp: eu.timestamp,
author: eu.author,
action_seq: eu.action_seq,
prev_action: eu.prev_action,
original_entry_address: eu.original_entry_address,
original_action_address: eu.original_action_address,
signature,
weight: eu.weight,
}
}
}
impl WireDelete {
pub fn into_record(self) -> Record {
Record::new(
SignedActionHashed::from_content_sync(SignedAction(self.delete.into(), self.signature)),
None,
)
}
}
impl WireUpdateRelationship {
pub fn into_record(self, original_entry_address: EntryHash) -> Record {
Record::new(
SignedActionHashed::from_content_sync(self.into_signed_action(original_entry_address)),
None,
)
}
pub fn into_signed_action(self, original_entry_address: EntryHash) -> SignedAction {
let eu = Update {
author: self.author,
timestamp: self.timestamp,
action_seq: self.action_seq,
prev_action: self.prev_action,
original_action_address: self.original_action_address,
original_entry_address,
entry_type: self.new_entry_type,
entry_hash: self.new_entry_address,
weight: self.weight,
};
SignedAction(Action::Update(eu), self.signature)
}
}
impl NewEntryActionRef<'_> {
pub fn entry_type(&self) -> &EntryType {
match self {
NewEntryActionRef::Create(Create { entry_type, .. })
| NewEntryActionRef::Update(Update { entry_type, .. }) => entry_type,
}
}
pub fn entry_hash(&self) -> &EntryHash {
match self {
NewEntryActionRef::Create(Create { entry_hash, .. })
| NewEntryActionRef::Update(Update { entry_hash, .. }) => entry_hash,
}
}
pub fn to_new_entry_action(&self) -> NewEntryAction {
match self {
NewEntryActionRef::Create(create) => NewEntryAction::Create((*create).to_owned()),
NewEntryActionRef::Update(update) => NewEntryAction::Update((*update).to_owned()),
}
}
}
impl TryFrom<SignedActionHashed> for WireDelete {
type Error = WrongActionError;
fn try_from(shh: SignedActionHashed) -> Result<Self, Self::Error> {
let (h, signature) = shh.into_inner();
Ok(Self {
delete: h.into_content().try_into()?,
signature,
})
}
}
impl TryFrom<SignedAction> for WireDelete {
type Error = WrongActionError;
fn try_from(sh: SignedAction) -> Result<Self, Self::Error> {
let SignedAction(h, signature) = sh;
Ok(Self {
delete: h.try_into()?,
signature,
})
}
}
impl TryFrom<SignedActionHashed> for WireUpdate {
type Error = WrongActionError;
fn try_from(shh: SignedActionHashed) -> Result<Self, Self::Error> {
let (h, signature) = shh.into_inner();
let d: Update = h.into_content().try_into()?;
Ok(Self {
signature,
timestamp: d.timestamp,
author: d.author,
action_seq: d.action_seq,
prev_action: d.prev_action,
original_entry_address: d.original_entry_address,
original_action_address: d.original_action_address,
weight: d.weight,
})
}
}
impl TryFrom<SignedActionHashed> for WireUpdateRelationship {
type Error = WrongActionError;
fn try_from(shh: SignedActionHashed) -> Result<Self, Self::Error> {
let (h, s) = shh.into_inner();
SignedAction(h.into_content(), s).try_into()
}
}
impl TryFrom<SignedAction> for WireUpdateRelationship {
type Error = WrongActionError;
fn try_from(sh: SignedAction) -> Result<Self, Self::Error> {
let SignedAction(h, signature) = sh;
let d: Update = h.try_into()?;
Ok(Self {
signature,
timestamp: d.timestamp,
author: d.author,
action_seq: d.action_seq,
prev_action: d.prev_action,
original_action_address: d.original_action_address,
new_entry_address: d.entry_hash,
new_entry_type: d.entry_type,
weight: d.weight,
})
}
}
impl WireNewEntryAction {
pub fn into_record(self, entry_type: EntryType, entry: Entry) -> Record {
let entry_hash = EntryHash::with_data_sync(&entry);
Record::new(self.into_action(entry_type, entry_hash), Some(entry))
}
pub fn into_action(self, entry_type: EntryType, entry_hash: EntryHash) -> SignedActionHashed {
SignedActionHashed::from_content_sync(self.into_signed_action(entry_type, entry_hash))
}
pub fn into_signed_action(self, entry_type: EntryType, entry_hash: EntryHash) -> SignedAction {
match self {
WireNewEntryAction::Create(ec) => {
let signature = ec.signature;
let ec = Create {
author: ec.author,
timestamp: ec.timestamp,
action_seq: ec.action_seq,
prev_action: ec.prev_action,
weight: ec.weight,
entry_type,
entry_hash,
};
SignedAction(ec.into(), signature)
}
WireNewEntryAction::Update(eu) => {
let signature = eu.signature;
let eu = Update {
author: eu.author,
timestamp: eu.timestamp,
action_seq: eu.action_seq,
prev_action: eu.prev_action,
original_entry_address: eu.original_entry_address,
original_action_address: eu.original_action_address,
weight: eu.weight,
entry_type,
entry_hash,
};
SignedAction(eu.into(), signature)
}
}
}
}
impl WireActionStatus<WireNewEntryAction> {
pub fn into_record_status(self, entry_type: EntryType, entry: Entry) -> RecordStatus {
RecordStatus::new(
self.action.into_record(entry_type, entry),
self.validation_status,
)
}
}
impl WireActionStatus<WireUpdateRelationship> {
pub fn into_record_status(self, entry_hash: EntryHash) -> RecordStatus {
RecordStatus::new(self.action.into_record(entry_hash), self.validation_status)
}
}
impl WireActionStatus<WireDelete> {
pub fn into_record_status(self) -> RecordStatus {
RecordStatus::new(self.action.into_record(), self.validation_status)
}
}
impl<H, W, E> TryFrom<(H, ValidationStatus)> for WireActionStatus<W>
where
E: Into<ActionError>,
H: TryInto<W, Error = E>,
{
type Error = ActionError;
fn try_from(value: (H, ValidationStatus)) -> Result<Self, Self::Error> {
Ok(Self::new(value.0.try_into().map_err(Into::into)?, value.1))
}
}
impl TryFrom<SignedActionHashed> for WireNewEntryAction {
type Error = ActionError;
fn try_from(shh: SignedActionHashed) -> Result<Self, Self::Error> {
let action = shh.hashed.content;
let signature = shh.signature;
match action {
Action::Create(ec) => Ok(Self::Create((ec, signature).into())),
Action::Update(eu) => Ok(Self::Update((eu, signature).into())),
_ => Err(ActionError::NotNewEntry),
}
}
}
impl TryFrom<SignedAction> for WireNewEntryAction {
type Error = ActionError;
fn try_from(sh: SignedAction) -> Result<Self, Self::Error> {
let (action, s) = sh.into();
match action {
Action::Create(ec) => Ok(Self::Create((ec, s).into())),
Action::Update(eu) => Ok(Self::Update((eu, s).into())),
_ => Err(ActionError::NotNewEntry),
}
}
}
impl TryFrom<Action> for NewEntryAction {
type Error = WrongActionError;
fn try_from(value: Action) -> Result<Self, Self::Error> {
match value {
Action::Create(h) => Ok(NewEntryAction::Create(h)),
Action::Update(h) => Ok(NewEntryAction::Update(h)),
_ => Err(WrongActionError(format!("{:?}", value))),
}
}
}
impl<'a> TryFrom<&'a Action> for NewEntryActionRef<'a> {
type Error = WrongActionError;
fn try_from(value: &'a Action) -> Result<Self, Self::Error> {
match value {
Action::Create(h) => Ok(NewEntryActionRef::Create(h)),
Action::Update(h) => Ok(NewEntryActionRef::Update(h)),
_ => Err(WrongActionError(format!("{:?}", value))),
}
}
}
impl<'a> From<&'a NewEntryAction> for NewEntryActionRef<'a> {
fn from(n: &'a NewEntryAction) -> Self {
match n {
NewEntryAction::Create(ec) => NewEntryActionRef::Create(ec),
NewEntryAction::Update(eu) => NewEntryActionRef::Update(eu),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fixt::ActionBuilderCommonFixturator;
use crate::test_utils::fake_dna_hash;
use crate::test_utils::fake_entry_hash;
use ::fixt::prelude::Unpredictable;
#[test]
fn test_action_msgpack_roundtrip() {
let orig: Action = Dna::from_builder(
fake_dna_hash(1),
ActionBuilderCommonFixturator::new(Unpredictable)
.next()
.unwrap(),
)
.into();
let bytes = holochain_serialized_bytes::encode(&orig).unwrap();
let res: Action = holochain_serialized_bytes::decode(&bytes).unwrap();
assert_eq!(orig, res);
}
#[test]
fn test_action_json_roundtrip() {
let orig: Action = Dna::from_builder(
fake_dna_hash(1),
ActionBuilderCommonFixturator::new(Unpredictable)
.next()
.unwrap(),
)
.into();
let orig = ActionHashed::from_content_sync(orig);
let json = serde_json::to_string(&orig).unwrap();
dbg!(&json);
let res: ActionHashed = serde_json::from_str(&json).unwrap();
assert_eq!(orig, res);
}
#[test]
fn test_create_entry_msgpack_roundtrip() {
let orig: Action = Create::from_builder(
ActionBuilderCommonFixturator::new(Unpredictable)
.next()
.unwrap(),
EntryType::App(AppEntryDef::new(
0.into(),
0.into(),
EntryVisibility::Public,
)),
fake_entry_hash(1).into(),
)
.into();
let bytes = holochain_serialized_bytes::encode(&orig).unwrap();
println!("{:?}", bytes);
let res: Action = holochain_serialized_bytes::decode(&bytes).unwrap();
assert_eq!(orig, res);
}
#[test]
fn test_create_entry_serializedbytes_roundtrip() {
let orig: Action = Create::from_builder(
ActionBuilderCommonFixturator::new(Unpredictable)
.next()
.unwrap(),
EntryType::App(AppEntryDef::new(
0.into(),
0.into(),
EntryVisibility::Public,
)),
fake_entry_hash(1).into(),
)
.into();
let bytes: SerializedBytes = orig.clone().try_into().unwrap();
let res: Action = bytes.try_into().unwrap();
assert_eq!(orig, res);
}
}