snix_castore/directoryservice/
traverse.rs1use crate::{Error, Node, Path, directoryservice::DirectoryService};
2use tracing::{instrument, warn};
3
4#[instrument(skip(directory_service, path), fields(%path))]
7pub async fn descend_to<DS>(
8 directory_service: DS,
9 root_node: Node,
10 path: impl AsRef<Path> + std::fmt::Display,
11) -> Result<Option<Node>, Error>
12where
13 DS: DirectoryService,
14{
15 let mut parent_node = root_node;
16 for component in path.as_ref().components_bytes() {
17 match parent_node {
18 Node::File { .. } | Node::Symlink { .. } => {
19 return Ok(None);
22 }
23 Node::Directory { digest, .. } => {
24 let directory = directory_service.get(&digest).await?.ok_or_else(|| {
26 warn!(directory.digest = %digest, "directory does not exist");
28
29 Error::StorageError(format!("directory {} does not exist", digest))
30 })?;
31
32 if let Some((_child_name, child_node)) = directory
34 .into_nodes()
35 .find(|(name, _node)| name.as_ref() == component)
36 {
37 parent_node = child_node.clone();
39 } else {
40 return Ok(None);
42 };
43 }
44 }
45 }
46
47 Ok(Some(parent_node))
49}
50
51#[cfg(test)]
52mod tests {
53 use crate::{
54 Node, PathBuf, directoryservice,
55 fixtures::{DIRECTORY_COMPLICATED, DIRECTORY_WITH_KEEP, EMPTY_BLOB_DIGEST},
56 };
57
58 use super::descend_to;
59
60 #[tokio::test]
61 async fn test_descend_to() {
62 let directory_service = directoryservice::from_addr("memory://").await.unwrap();
63
64 let mut handle = directory_service.put_multiple_start();
65 handle
66 .put(DIRECTORY_WITH_KEEP.clone())
67 .await
68 .expect("must succeed");
69 handle
70 .put(DIRECTORY_COMPLICATED.clone())
71 .await
72 .expect("must succeed");
73
74 handle.close().await.expect("must upload");
75
76 let node_directory_complicated = Node::Directory {
78 digest: DIRECTORY_COMPLICATED.digest(),
79 size: DIRECTORY_COMPLICATED.size(),
80 };
81
82 let node_directory_with_keep = Node::Directory {
84 digest: DIRECTORY_WITH_KEEP.digest(),
85 size: DIRECTORY_WITH_KEEP.size(),
86 };
87
88 let node_file_keep = Node::File {
90 digest: EMPTY_BLOB_DIGEST.clone(),
91 size: 0,
92 executable: false,
93 };
94
95 {
97 let resp = descend_to(
98 &directory_service,
99 node_directory_complicated.clone(),
100 "".parse::<PathBuf>().unwrap(),
101 )
102 .await
103 .expect("must succeed");
104
105 assert_eq!(Some(node_directory_complicated.clone()), resp);
106 }
107
108 {
110 let resp = descend_to(
111 &directory_service,
112 node_directory_complicated.clone(),
113 "keep".parse::<PathBuf>().unwrap(),
114 )
115 .await
116 .expect("must succeed");
117
118 assert_eq!(Some(node_directory_with_keep), resp);
119 }
120
121 {
123 let resp = descend_to(
124 &directory_service,
125 node_directory_complicated.clone(),
126 "keep/.keep".parse::<PathBuf>().unwrap(),
127 )
128 .await
129 .expect("must succeed");
130
131 assert_eq!(Some(node_file_keep.clone()), resp);
132 }
133
134 {
136 let resp = descend_to(
137 &directory_service,
138 node_directory_complicated.clone(),
139 "void".parse::<PathBuf>().unwrap(),
140 )
141 .await
142 .expect("must succeed");
143
144 assert_eq!(None, resp);
145 }
146
147 {
149 let resp = descend_to(
150 &directory_service,
151 node_directory_complicated.clone(),
152 "v/oid".parse::<PathBuf>().unwrap(),
153 )
154 .await
155 .expect("must succeed");
156
157 assert_eq!(None, resp);
158 }
159
160 {
163 let resp = descend_to(
164 &directory_service,
165 node_directory_complicated.clone(),
166 "keep/.keep/foo".parse::<PathBuf>().unwrap(),
167 )
168 .await
169 .expect("must succeed");
170
171 assert_eq!(None, resp);
172 }
173 }
174}