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