1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
use crate::prelude::*;
use hdi::hash_path::path::{root_hash, Component, TypedPath};
pub trait HdkPathExt {
fn children(&self) -> ExternResult<Vec<holochain_zome_types::link::Link>>;
fn children_paths(&self) -> ExternResult<Vec<TypedPath>>;
fn children_details(&self) -> ExternResult<holochain_zome_types::link::LinkDetails>;
fn ensure(&self) -> ExternResult<()>;
fn exists(&self) -> ExternResult<bool>;
}
impl HdkPathExt for TypedPath {
/// Touch and list all the links from this path to paths below it.
/// Only returns links between paths, not to other entries that might have their own links.
fn children(&self) -> ExternResult<Vec<holochain_zome_types::link::Link>> {
Self::ensure(self)?;
let mut unwrapped = get_links(
LinkQuery::new(
self.path_entry_hash()?,
LinkTypeFilter::single_type(self.link_type.zome_index, self.link_type.zome_type),
),
self.strategy,
)?;
// Only need one of each hash to build the tree.
unwrapped.sort_unstable_by(|a, b| a.tag.cmp(&b.tag));
unwrapped.dedup_by(|a, b| a.tag.eq(&b.tag));
Ok(unwrapped)
}
/// Touch and list all the links from this path to paths below it.
/// Same as `Path::children` but returns `Vec<Path>` rather than `Vec<Link>`.
/// This is more than just a convenience. In general it's not possible to
/// construct a full `Path` from a child `Link` alone as only a single
/// `Component` is encoded into the link tag. To build a full child path
/// the parent path + child link must be combined, which this function does
/// to produce each child, by using `&self` as that parent.
fn children_paths(&self) -> ExternResult<Vec<TypedPath>> {
let children = self.children()?;
let components: ExternResult<Vec<Option<Component>>> = children
.into_iter()
.map(|link| {
let component_bytes = &link.tag.0[..];
if component_bytes.is_empty() {
Ok(None)
} else {
Ok(Some(
SerializedBytes::from(UnsafeBytes::from(component_bytes.to_vec()))
.try_into()
.map_err(|e: SerializedBytesError| wasm_error!(e))?,
))
}
})
.collect();
Ok(components?
.into_iter()
.map(|maybe_component| {
let mut new_path = self.path.clone();
if let Some(component) = maybe_component {
new_path.append_component(component);
}
new_path.into_typed(self.link_type)
})
.collect())
}
fn children_details(&self) -> ExternResult<holochain_zome_types::link::LinkDetails> {
Self::ensure(self)?;
get_links_details(
LinkQuery::new(
self.path_entry_hash()?,
LinkTypeFilter::single_type(self.link_type.zome_index, self.link_type.zome_type),
)
.tag_prefix(holochain_zome_types::link::LinkTag::new([])),
self.strategy,
)
}
/// Ensures that this path exists by recursively creating missing parent links.
///
/// This function checks whether the current path already exists.
/// If it does not, it recursively ensures that all parent paths exist and then
/// creates the appropriate link for this path.
///
/// The behavior depends on whether the path is the root:
///
/// - If the path is the root and does not exist, a link is created from the
/// global root hash to this path entry.
/// - If the path is not the root, its parent is first ensured recursively by calling [`Self::ensure`] on it,
/// and then a link is created from the parent path entry to this path entry.
///
/// If the path already exists, this function is a no-op.
///
/// # Errors
///
/// Returns an error if:
/// - Checking for existence fails
/// - [`Self::ensure`] on a parent path fails
/// - Creating any required link fails
///
/// # Notes
///
/// This function does **not** create entries; it only creates links at
/// deterministic path hashes.
///
/// `Path` operates on so-called *ghost entries*: no entry is ever written to
/// the DHT for a path itself. Instead, the hash that *would* correspond to a
/// path entry is deterministically derived and used as the base or target
/// address for links.
///
/// In other words, [`Path::path_entry_hash`] does not require a prior
/// entry create; it computes a stable hash that is used purely as a link
/// anchor in the DHT.
///
/// The operation is idempotent: calling [`Self::ensure`] multiple times for the same
/// path will not create duplicate links, given that [`Self::exists`] correctly
/// reflects the current state of the DHT.
fn ensure(&self) -> ExternResult<()> {
if !self.exists()? {
if self.is_root() {
create_link(
root_hash()?,
self.path_entry_hash()?,
self.link_type,
self.make_tag()?,
)?;
} else if let Some(parent) = self.parent() {
parent.ensure()?;
create_link(
parent.path_entry_hash()?,
self.path_entry_hash()?,
self.link_type,
self.make_tag()?,
)?;
}
}
Ok(())
}
/// Does data exist at the hash we expect?
fn exists(&self) -> ExternResult<bool> {
if self.0.is_empty() {
Ok(false)
} else if self.is_root() {
let this_paths_hash: AnyLinkableHash = self.path_entry_hash()?.into();
let exists = get_links(
LinkQuery::new(
root_hash()?,
LinkTypeFilter::single_type(
self.link_type.zome_index,
self.link_type.zome_type,
),
)
.tag_prefix(self.make_tag()?),
self.strategy,
)?
.iter()
.any(|Link { target, .. }| *target == this_paths_hash);
Ok(exists)
} else {
let parent = self
.parent()
.expect("Must have parent if not empty or root");
let this_paths_hash: AnyLinkableHash = self.path_entry_hash()?.into();
let exists = get_links(
LinkQuery::new(
parent.path_entry_hash()?,
LinkTypeFilter::single_type(
self.link_type.zome_index,
self.link_type.zome_type,
),
)
.tag_prefix(self.make_tag()?),
self.strategy,
)?
.iter()
.any(|Link { target, .. }| *target == this_paths_hash);
Ok(exists)
}
}
}