hdk/hash_path/
path.rs

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    /// Touch and list all the links from this path to paths below it.
14    /// Only returns links between paths, not to other entries that might have their own links.
15    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        // Only need one of each hash to build the tree.
27        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    /// Touch and list all the links from this path to paths below it.
33    /// Same as `Path::children` but returns `Vec<Path>` rather than `Vec<Link>`.
34    /// This is more than just a convenience. In general it's not possible to
35    /// construct a full `Path` from a child `Link` alone as only a single
36    /// `Component` is encoded into the link tag. To build a full child path
37    /// the parent path + child link must be combined, which this function does
38    /// to produce each child, by using `&self` as that parent.
39    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    /// Recursively touch this and every parent that doesn't exist yet.
81    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    /// Does data exist at the hash we expect?
104    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}