1use crate::prelude::*;
2use hdi::hash_path::path::{root_hash, Component, TypedPath};
3
4pub trait HdkPathExt {
5 fn children(&self) -> ExternResult<Vec<holochain_zome_types::link::Link>>;
6 fn children_paths(&self) -> ExternResult<Vec<TypedPath>>;
7 fn children_details(&self) -> ExternResult<holochain_zome_types::link::LinkDetails>;
8 fn ensure(&self) -> ExternResult<()>;
9 fn exists(&self) -> ExternResult<bool>;
10}
11
12impl HdkPathExt for TypedPath {
13 fn children(&self) -> ExternResult<Vec<holochain_zome_types::link::Link>> {
16 Self::ensure(self)?;
17
18 let mut unwrapped = get_links(
19 LinkQuery::new(
20 self.path_entry_hash()?,
21 LinkTypeFilter::single_type(self.link_type.zome_index, self.link_type.zome_type),
22 ),
23 GetStrategy::default(),
24 )?;
25
26 unwrapped.sort_unstable_by(|a, b| a.tag.cmp(&b.tag));
28 unwrapped.dedup_by(|a, b| a.tag.eq(&b.tag));
29 Ok(unwrapped)
30 }
31
32 fn children_paths(&self) -> ExternResult<Vec<TypedPath>> {
40 let children = self.children()?;
41 let components: ExternResult<Vec<Option<Component>>> = children
42 .into_iter()
43 .map(|link| {
44 let component_bytes = &link.tag.0[..];
45 if component_bytes.is_empty() {
46 Ok(None)
47 } else {
48 Ok(Some(
49 SerializedBytes::from(UnsafeBytes::from(component_bytes.to_vec()))
50 .try_into()
51 .map_err(|e: SerializedBytesError| wasm_error!(e))?,
52 ))
53 }
54 })
55 .collect();
56 Ok(components?
57 .into_iter()
58 .map(|maybe_component| {
59 let mut new_path = self.path.clone();
60 if let Some(component) = maybe_component {
61 new_path.append_component(component);
62 }
63 new_path.into_typed(self.link_type)
64 })
65 .collect())
66 }
67
68 fn children_details(&self) -> ExternResult<holochain_zome_types::link::LinkDetails> {
69 Self::ensure(self)?;
70 get_links_details(
71 LinkQuery::new(
72 self.path_entry_hash()?,
73 LinkTypeFilter::single_type(self.link_type.zome_index, self.link_type.zome_type),
74 )
75 .tag_prefix(holochain_zome_types::link::LinkTag::new([])),
76 GetStrategy::default(),
77 )
78 }
79
80 fn ensure(&self) -> ExternResult<()> {
82 if !self.exists()? {
83 if self.is_root() {
84 create_link(
85 root_hash()?,
86 self.path_entry_hash()?,
87 self.link_type,
88 self.make_tag()?,
89 )?;
90 } else if let Some(parent) = self.parent() {
91 parent.ensure()?;
92 create_link(
93 parent.path_entry_hash()?,
94 self.path_entry_hash()?,
95 self.link_type,
96 self.make_tag()?,
97 )?;
98 }
99 }
100 Ok(())
101 }
102
103 fn exists(&self) -> ExternResult<bool> {
105 if self.0.is_empty() {
106 Ok(false)
107 } else if self.is_root() {
108 let this_paths_hash: AnyLinkableHash = self.path_entry_hash()?.into();
109 let exists = get_links(
110 LinkQuery::new(
111 root_hash()?,
112 LinkTypeFilter::single_type(
113 self.link_type.zome_index,
114 self.link_type.zome_type,
115 ),
116 )
117 .tag_prefix(self.make_tag()?),
118 GetStrategy::default(),
119 )?
120 .iter()
121 .any(|Link { target, .. }| *target == this_paths_hash);
122 Ok(exists)
123 } else {
124 let parent = self
125 .parent()
126 .expect("Must have parent if not empty or root");
127 let this_paths_hash: AnyLinkableHash = self.path_entry_hash()?.into();
128 let exists = get_links(
129 LinkQuery::new(
130 parent.path_entry_hash()?,
131 LinkTypeFilter::single_type(
132 self.link_type.zome_index,
133 self.link_type.zome_type,
134 ),
135 )
136 .tag_prefix(self.make_tag()?),
137 GetStrategy::default(),
138 )?
139 .iter()
140 .any(|Link { target, .. }| *target == this_paths_hash);
141 Ok(exists)
142 }
143 }
144}