use holo_hash::ActionHash;
use holo_hash::AgentPubKey;
use holo_hash::AnyLinkableHash;
use holo_hash::EntryHash;
use holochain_serialized_bytes::prelude::*;
use holochain_zome_types::prelude::*;
use regex::Regex;
use crate::dht_op::error::DhtOpError;
use crate::dht_op::error::DhtOpResult;
use crate::dht_op::DhtOpType;
use crate::dht_op::RenderedOp;
use crate::dht_op::RenderedOps;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, SerializedBytes)]
pub struct Link {
base: EntryHash,
target: EntryHash,
tag: LinkTag,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
pub struct WireLinkKey {
pub base: AnyLinkableHash,
pub type_query: LinkTypeFilter,
pub tag: Option<LinkTag>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes, Default)]
pub struct WireLinkOps {
pub creates: Vec<WireCreateLink>,
pub deletes: Vec<WireDeleteLink>,
}
impl WireLinkOps {
pub fn new() -> Self {
Default::default()
}
pub fn render(self, key: &WireLinkKey) -> DhtOpResult<RenderedOps> {
let Self { creates, deletes } = self;
let mut ops = Vec::with_capacity(creates.len() + deletes.len());
ops.extend(creates.into_iter().filter_map(|op| op.render(key).ok()));
ops.extend(deletes.into_iter().filter_map(|op| op.render(key).ok()));
Ok(RenderedOps {
ops,
..Default::default()
})
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
pub struct WireCreateLink {
pub author: AgentPubKey,
pub timestamp: Timestamp,
pub action_seq: u32,
pub prev_action: ActionHash,
pub target_address: AnyLinkableHash,
pub zome_index: ZomeIndex,
pub link_type: LinkType,
pub tag: Option<LinkTag>,
pub signature: Signature,
pub validation_status: ValidationStatus,
pub weight: RateWeight,
}
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
pub struct WireDeleteLink {
pub author: AgentPubKey,
pub timestamp: Timestamp,
pub action_seq: u32,
pub prev_action: ActionHash,
pub link_add_address: ActionHash,
pub signature: Signature,
pub validation_status: ValidationStatus,
}
impl WireCreateLink {
fn new(
h: CreateLink,
signature: Signature,
validation_status: ValidationStatus,
tag: bool,
) -> Self {
Self {
author: h.author,
timestamp: h.timestamp,
action_seq: h.action_seq,
prev_action: h.prev_action,
target_address: h.target_address,
zome_index: h.zome_index,
link_type: h.link_type,
tag: if tag { Some(h.tag) } else { None },
signature,
validation_status,
weight: h.weight,
}
}
pub fn condense_base_only(
h: CreateLink,
signature: Signature,
validation_status: ValidationStatus,
) -> Self {
Self::new(h, signature, validation_status, false)
}
pub fn condense(
h: CreateLink,
signature: Signature,
validation_status: ValidationStatus,
) -> Self {
Self::new(h, signature, validation_status, true)
}
pub fn render(self, key: &WireLinkKey) -> DhtOpResult<RenderedOp> {
let tag = self
.tag
.or_else(|| key.tag.clone())
.ok_or(DhtOpError::LinkKeyTagMissing)?;
let action = Action::CreateLink(CreateLink {
author: self.author,
timestamp: self.timestamp,
action_seq: self.action_seq,
prev_action: self.prev_action,
base_address: key.base.clone(),
target_address: self.target_address,
zome_index: self.zome_index,
link_type: self.link_type,
weight: self.weight,
tag,
});
let signature = self.signature;
let validation_status = Some(self.validation_status);
RenderedOp::new(
action,
signature,
validation_status,
DhtOpType::RegisterAddLink,
)
}
}
impl WireDeleteLink {
pub fn condense(
h: DeleteLink,
signature: Signature,
validation_status: ValidationStatus,
) -> Self {
Self {
author: h.author,
timestamp: h.timestamp,
action_seq: h.action_seq,
prev_action: h.prev_action,
signature,
validation_status,
link_add_address: h.link_add_address,
}
}
pub fn render(self, key: &WireLinkKey) -> DhtOpResult<RenderedOp> {
let action = Action::DeleteLink(DeleteLink {
author: self.author,
timestamp: self.timestamp,
action_seq: self.action_seq,
prev_action: self.prev_action,
base_address: key.base.clone(),
link_add_address: self.link_add_address,
});
let signature = self.signature;
let validation_status = Some(self.validation_status);
RenderedOp::new(
action,
signature,
validation_status,
DhtOpType::RegisterRemoveLink,
)
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
pub struct GetLinksResponse {
pub link_adds: Vec<(CreateLink, Signature)>,
pub link_removes: Vec<(DeleteLink, Signature)>,
}
impl Link {
pub fn new(base: &EntryHash, target: &EntryHash, tag: &LinkTag) -> Self {
Link {
base: base.to_owned(),
target: target.to_owned(),
tag: tag.to_owned(),
}
}
pub fn base(&self) -> &EntryHash {
&self.base
}
pub fn target(&self) -> &EntryHash {
&self.target
}
pub fn tag(&self) -> &LinkTag {
&self.tag
}
}
pub enum LinkMatch<S: Into<String>> {
Any,
Exactly(S),
Regex(S),
}
impl<S: Into<String>> LinkMatch<S> {
#[allow(clippy::wrong_self_convention)]
pub fn to_regex_string(self) -> Result<String, String> {
let re_string: String = match self {
LinkMatch::Any => ".*".into(),
LinkMatch::Exactly(s) => "^".to_owned() + ®ex::escape(&s.into()) + "$",
LinkMatch::Regex(s) => s.into(),
};
match Regex::new(&re_string) {
Ok(_) => Ok(re_string),
Err(_) => Err("Invalid regex passed to get_links".into()),
}
}
}
#[derive(serde::Serialize, serde::Deserialize, SerializedBytes, PartialEq, Clone, Debug)]
pub struct WireLinkQuery {
pub base: AnyLinkableHash,
pub link_type: LinkTypeFilter,
pub tag_prefix: Option<LinkTag>,
pub before: Option<Timestamp>,
pub after: Option<Timestamp>,
pub author: Option<AgentPubKey>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
pub struct CountLinksResponse(Vec<ActionHash>);
impl CountLinksResponse {
pub fn new(create_link_actions: Vec<ActionHash>) -> Self {
CountLinksResponse(create_link_actions)
}
pub fn create_link_actions(&self) -> Vec<ActionHash> {
self.0.clone()
}
}