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