1use std::convert::TryFrom;
2use hdk::prelude::*;
3use crate::errors::{
4 UtilsResult, UtilsError,
5};
6
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct EntityType {
11 pub name: String,
13
14 pub model: String,
16}
17
18pub trait EntryModel<T>
20where
21 ScopedEntryDefIndex: for<'a> TryFrom<&'a T, Error = WasmError>,
22{
23 fn name() -> &'static str;
24 fn get_type(&self) -> EntityType;
25 fn to_input(&self) -> T;
26}
27
28impl EntityType {
29 pub fn new(name: &'static str, model: &'static str) -> Self {
30 EntityType {
31 name: name.into(),
32 model: model.into(),
33 }
34 }
35}
36
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct Entity<T> {
41 pub id: EntryHash,
43
44 pub action: ActionHash,
46
47 pub address: EntryHash,
49
50 #[serde(rename = "type")]
51 pub ctype: EntityType,
53
54 pub content: T,
56}
57
58impl<T> Entity<T> {
59
60 pub fn link_from<L,E>(&self, base: &EntryHash, link_type: L, tag_input: Option<Vec<u8>>) -> UtilsResult<ActionHash>
62 where
63 ScopedLinkType: TryFrom<L, Error = E>,
64 WasmError: From<E>,
65 {
66 Ok( match tag_input {
67 None => create_link( base.to_owned(), self.id.to_owned(), link_type, () )?,
68 Some(input) => create_link( base.to_owned(), self.id.to_owned(), link_type, input )?,
69 })
70 }
71
72 pub fn link_to<L,E>(&self, target: &EntryHash, link_type: L, tag_input: Option<Vec<u8>>) -> UtilsResult<ActionHash>
74 where
75 ScopedLinkType: TryFrom<L, Error = E>,
76 WasmError: From<E>,
77 {
78 Ok( match tag_input {
79 None => create_link( self.id.to_owned(), target.to_owned(), link_type, () )?,
80 Some(input) => create_link( self.id.to_owned(), target.to_owned(), link_type, input )?,
81 })
82 }
83
84 pub fn move_link_from<LT,E>(&self, link_type: LT, tag_input: Option<Vec<u8>>, current_base: &EntryHash, new_base: &EntryHash) -> UtilsResult<ActionHash>
86 where
87 LT: LinkTypeFilterExt + Clone + std::fmt::Debug,
88 ScopedLinkType: TryFrom<LT, Error = E>,
89 WasmError: From<E>,
90 {
91 let tag_filter = tag_input.to_owned().map( |tag| LinkTag::new( tag ) );
92 let all_links = get_links(
93 current_base.clone(),
94 link_type.to_owned(),
95 tag_filter.to_owned(),
96 )?;
97
98 if let Some(current_link) = all_links.into_iter().find(|link| {
99 link.target == self.id.to_owned().into()
100 }) {
101 delete_link( current_link.create_link_hash )?;
102 }
103 else {
104 Err(UtilsError::UnexpectedState(format!("Aborting 'move_from_link' because existing link was not found")))?;
105 };
106
107 let new_links = get_links(
108 new_base.clone(),
109 link_type.to_owned(),
110 tag_filter.to_owned(),
111 )?;
112
113 if let Some(existing_link) = new_links.into_iter().find(|link| {
114 link.target == self.id.to_owned().into()
115 }) {
116 Ok( existing_link.create_link_hash )
117 }
118 else {
119 self.link_from( new_base, link_type, tag_input )
120 }
121 }
122}
123
124
125#[derive(Clone, Debug, Serialize, Deserialize)]
126pub struct Empty {}
127
128pub type EmptyEntity = Entity<Empty>;
131
132
133
134#[cfg(test)]
135pub mod tests {
136 use super::*;
137 use rand::Rng;
138
139 #[test]
140 fn entity_test() {
141 let bytes = rand::thread_rng().gen::<[u8; 32]>();
142 let ehash = holo_hash::EntryHash::from_raw_32( bytes.to_vec() );
143 let hhash = holo_hash::ActionHash::from_raw_32( bytes.to_vec() );
144
145 let item = Entity {
146 id: ehash.clone(),
147 action: hhash,
148 address: ehash,
149 ctype: EntityType::new( "boolean", "primitive" ),
150 content: true,
151 };
152
153 assert_eq!( item.ctype.name, "boolean" );
154 assert_eq!( item.ctype.model, "primitive" );
155 }
156}