@anysphere/file-service 0.0.0-e3fdf62d → 0.0.0-e6124fba
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +12 -0
- package/build.rs +2 -0
- package/index.d.ts +7 -3
- package/package.json +10 -8
- package/src/file_utils.rs +141 -27
- package/src/git_utils.rs +168 -20
- package/src/lib.rs +166 -16
- package/src/merkle_tree/local_construction.rs +46 -9
- package/src/merkle_tree/mod.rs +326 -115
- package/src/merkle_tree/test.rs +2 -1
- package/src/test.rs +5 -0
package/src/merkle_tree/mod.rs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
use super::file_utils;
|
|
2
2
|
use sha2::Digest;
|
|
3
|
+
use std::collections::{BTreeMap, HashSet};
|
|
3
4
|
use std::path::PathBuf;
|
|
4
|
-
use std::
|
|
5
|
+
use std::vec;
|
|
6
|
+
use std::{fs, path::Path, sync::Arc};
|
|
5
7
|
use tokio::sync::RwLock;
|
|
6
8
|
use tonic::async_trait;
|
|
9
|
+
use tracing::info;
|
|
10
|
+
|
|
7
11
|
pub mod local_construction;
|
|
8
12
|
pub mod test;
|
|
9
13
|
|
|
@@ -12,8 +16,10 @@ pub type MerkleNodePtr = Arc<RwLock<MerkleNode>>;
|
|
|
12
16
|
pub struct MerkleTree {
|
|
13
17
|
root_path: String,
|
|
14
18
|
root: MerkleNodePtr,
|
|
15
|
-
files:
|
|
16
|
-
cursor: Option<
|
|
19
|
+
files: BTreeMap<String, File>,
|
|
20
|
+
cursor: Option<usize>,
|
|
21
|
+
git_ignored_files_and_dirs: HashSet<String>,
|
|
22
|
+
is_git_repo: bool,
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
#[derive(Debug)]
|
|
@@ -57,6 +63,8 @@ pub trait LocalConstruction {
|
|
|
57
63
|
|
|
58
64
|
async fn construct_merkle_tree(
|
|
59
65
|
root_directory: String,
|
|
66
|
+
git_ignored_files_and_dirs: HashSet<String>,
|
|
67
|
+
is_git_repo: bool
|
|
60
68
|
) -> Result<MerkleTree, anyhow::Error>;
|
|
61
69
|
|
|
62
70
|
async fn update_file(
|
|
@@ -87,27 +95,38 @@ impl MerkleTree {
|
|
|
87
95
|
pub fn empty_tree() -> MerkleTree {
|
|
88
96
|
MerkleTree {
|
|
89
97
|
root: Arc::new(RwLock::new(MerkleNode::empty_node(None, None))),
|
|
90
|
-
files:
|
|
98
|
+
files: BTreeMap::new(),
|
|
91
99
|
root_path: "".to_string(),
|
|
92
100
|
cursor: None,
|
|
101
|
+
git_ignored_files_and_dirs: HashSet::new(),
|
|
102
|
+
is_git_repo: false
|
|
93
103
|
}
|
|
94
104
|
}
|
|
95
105
|
|
|
96
106
|
pub async fn get_subtree_hash(
|
|
97
107
|
&self,
|
|
98
|
-
|
|
108
|
+
absolute_path: &str,
|
|
99
109
|
) -> Result<String, anyhow::Error> {
|
|
100
|
-
let
|
|
101
|
-
let node = match self.files.get(path.to_str().unwrap()) {
|
|
110
|
+
let node = match self.files.get(absolute_path) {
|
|
102
111
|
Some(file) => file.node.clone(),
|
|
103
112
|
None => {
|
|
104
|
-
|
|
113
|
+
let all_files: Vec<String> = self.files.keys().cloned().collect();
|
|
114
|
+
return Err(anyhow::anyhow!(
|
|
115
|
+
"Could not find file in tree! Looking for: {}. All files: {:?}",
|
|
116
|
+
absolute_path,
|
|
117
|
+
all_files
|
|
118
|
+
));
|
|
105
119
|
}
|
|
106
120
|
};
|
|
107
121
|
|
|
108
122
|
let node_reader = node.read().await;
|
|
109
123
|
let node_hash = node_reader.hash.clone();
|
|
110
124
|
|
|
125
|
+
info!(
|
|
126
|
+
"get_subtree_hash for path: {}, node_hash: {}",
|
|
127
|
+
absolute_path, node_hash
|
|
128
|
+
);
|
|
129
|
+
|
|
111
130
|
Ok(node_hash)
|
|
112
131
|
}
|
|
113
132
|
|
|
@@ -132,6 +151,43 @@ impl MerkleTree {
|
|
|
132
151
|
Ok(count)
|
|
133
152
|
}
|
|
134
153
|
|
|
154
|
+
pub async fn get_num_embeddable_files_in_subtree(
|
|
155
|
+
&self,
|
|
156
|
+
absolute_path: PathBuf,
|
|
157
|
+
) -> Result<i32, anyhow::Error> {
|
|
158
|
+
let mut count = 0;
|
|
159
|
+
|
|
160
|
+
let absolute_path = match absolute_path.to_str() {
|
|
161
|
+
Some(s) => s.to_string(),
|
|
162
|
+
None => {
|
|
163
|
+
return Err(anyhow::anyhow!(
|
|
164
|
+
"get_num_embeddable_files_in_subtree: Failed to convert path to string"
|
|
165
|
+
))
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// TODO(sualeh): worth keeping this list sorted. its now a btree
|
|
170
|
+
|
|
171
|
+
for (_, file) in &self.files {
|
|
172
|
+
let file_reader = file.node.read().await;
|
|
173
|
+
match &file_reader.node_type {
|
|
174
|
+
NodeType::File(file_name) => {
|
|
175
|
+
if file_name.contains(&absolute_path) {
|
|
176
|
+
count += 1;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
NodeType::Branch(_) => {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
NodeType::ErrorNode(_) => {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
Ok(count)
|
|
189
|
+
}
|
|
190
|
+
|
|
135
191
|
pub async fn get_all_files(&self) -> Result<Vec<String>, anyhow::Error> {
|
|
136
192
|
let mut files = Vec::new();
|
|
137
193
|
|
|
@@ -188,83 +244,120 @@ impl MerkleTree {
|
|
|
188
244
|
pub async fn get_next_file_to_embed(
|
|
189
245
|
&mut self,
|
|
190
246
|
) -> Result<(String, Vec<String>), anyhow::Error> {
|
|
191
|
-
// the
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
let
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
NodeType::Branch(branch) => branch.clone(),
|
|
211
|
-
NodeType::File(_) => {
|
|
212
|
-
return Err(anyhow::anyhow!(
|
|
213
|
-
"get_next_file_to_embed: This should not happen! the branch happened to be file."
|
|
214
|
-
));
|
|
215
|
-
}
|
|
216
|
-
NodeType::ErrorNode(_) => {
|
|
217
|
-
return Err(anyhow::anyhow!("Cursor is an error node!"));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
};
|
|
247
|
+
// if the cursor is none, set it to 0
|
|
248
|
+
let cursor = match self.cursor {
|
|
249
|
+
Some(cursor) => cursor,
|
|
250
|
+
None => {
|
|
251
|
+
self.cursor = Some(0);
|
|
252
|
+
0
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// get the thing at the cursor. while we dont find a file, we keep incrementing the cursor.
|
|
257
|
+
let mut cursor = cursor;
|
|
258
|
+
loop {
|
|
259
|
+
// O(log n)
|
|
260
|
+
let file = match self.files.values().nth(cursor) {
|
|
261
|
+
Some(file) => file,
|
|
262
|
+
None => {
|
|
263
|
+
return Err(anyhow::anyhow!("Could not find file to embed!"));
|
|
264
|
+
}
|
|
265
|
+
};
|
|
221
266
|
|
|
222
|
-
|
|
223
|
-
|
|
267
|
+
let file_reader = file.node.read().await;
|
|
268
|
+
match &file_reader.node_type {
|
|
269
|
+
NodeType::File(f) => {
|
|
270
|
+
// update the cursor.
|
|
271
|
+
self.cursor = Some(cursor + 1);
|
|
272
|
+
let spline = self.get_spline(f).await?;
|
|
273
|
+
return Ok((f.clone(), spline));
|
|
274
|
+
}
|
|
275
|
+
NodeType::Branch(_) => {
|
|
276
|
+
cursor += 1;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
NodeType::ErrorNode(_) => {
|
|
280
|
+
cursor += 1;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
224
286
|
|
|
225
|
-
|
|
226
|
-
|
|
287
|
+
pub async fn get_all_dir_files_to_embed(
|
|
288
|
+
&self,
|
|
289
|
+
absolute_path: &str,
|
|
290
|
+
) -> Result<Vec<String>, anyhow::Error> {
|
|
291
|
+
let mut files = Vec::new();
|
|
227
292
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
293
|
+
// 1. should check that this absolute path is actually a directory.
|
|
294
|
+
let file_node = self.files.get(absolute_path);
|
|
295
|
+
if file_node.is_none() {
|
|
296
|
+
return Err(anyhow::anyhow!("Could not find directory the in tree!"));
|
|
297
|
+
}
|
|
232
298
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
is_branch = true;
|
|
299
|
+
for (file_path, f) in &self.files {
|
|
300
|
+
if !file_path.contains(absolute_path) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
238
303
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return Err(anyhow::anyhow!("Root has no children!"));
|
|
304
|
+
match f.node.read().await.node_type {
|
|
305
|
+
NodeType::File(_) => {
|
|
306
|
+
files.push(file_path.clone());
|
|
307
|
+
}
|
|
308
|
+
NodeType::Branch(_) => {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
NodeType::ErrorNode(_) => {
|
|
312
|
+
continue;
|
|
249
313
|
}
|
|
250
314
|
}
|
|
251
315
|
}
|
|
252
316
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// UNWRAP checked and fine. see the none case above.
|
|
257
|
-
let cursor_name = self.cursor.as_ref().unwrap();
|
|
258
|
-
let cursor_reader = cursor_name.read().await;
|
|
317
|
+
Ok(files)
|
|
318
|
+
}
|
|
259
319
|
|
|
260
|
-
|
|
320
|
+
// TODO(sualeh): i need tests for this!!
|
|
321
|
+
pub async fn get_spline(
|
|
322
|
+
&self,
|
|
323
|
+
absolute_path: &str,
|
|
324
|
+
) -> Result<Vec<String>, anyhow::Error> {
|
|
325
|
+
let mut files = Vec::new();
|
|
261
326
|
|
|
262
|
-
|
|
327
|
+
let current_node = match self.files.get(absolute_path) {
|
|
328
|
+
Some(node) => {
|
|
329
|
+
node.node.clone()
|
|
330
|
+
}
|
|
331
|
+
None => {
|
|
332
|
+
return Err(anyhow::anyhow!("File not found: {}", absolute_path));
|
|
333
|
+
}
|
|
334
|
+
};
|
|
263
335
|
|
|
264
|
-
|
|
265
|
-
|
|
336
|
+
let mut stack = Vec::new();
|
|
337
|
+
stack.push(current_node);
|
|
338
|
+
|
|
339
|
+
while let Some(node) = stack.pop() {
|
|
340
|
+
let parent = node.read().await.parent.clone();
|
|
341
|
+
if let Some(parent) = parent {
|
|
342
|
+
{
|
|
343
|
+
let parent_node = parent.read().await;
|
|
344
|
+
match &parent_node.node_type {
|
|
345
|
+
NodeType::File(file_name) => {
|
|
346
|
+
files.push(file_name.clone());
|
|
347
|
+
}
|
|
348
|
+
NodeType::Branch((branch_name, _)) => {
|
|
349
|
+
files.push(branch_name.clone());
|
|
350
|
+
}
|
|
351
|
+
_ => {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
266
356
|
|
|
267
|
-
|
|
357
|
+
stack.push(parent);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
Ok(files)
|
|
268
361
|
}
|
|
269
362
|
|
|
270
363
|
/// creates a new node and attaches it to the current tree.
|
|
@@ -302,12 +395,19 @@ impl MerkleTree {
|
|
|
302
395
|
// 1. the path is empty. this means that the ancestor is the root.
|
|
303
396
|
// 2. the path is non-empty. that means there exist a non-empty element btwn till the root.
|
|
304
397
|
|
|
398
|
+
let absolute_root_path = self.root_path.clone();
|
|
305
399
|
let new_node = match path.len() {
|
|
306
400
|
0 => {
|
|
307
401
|
// this means that the ancestor is the root.
|
|
308
402
|
// we need to create a new node and attach it to the ancestor.
|
|
309
|
-
let new_node =
|
|
310
|
-
|
|
403
|
+
let new_node = MerkleNode::new(
|
|
404
|
+
file_path.clone(),
|
|
405
|
+
Some(ancestor.clone()),
|
|
406
|
+
&self.git_ignored_files_and_dirs,
|
|
407
|
+
&absolute_root_path.as_str(),
|
|
408
|
+
self.is_git_repo
|
|
409
|
+
)
|
|
410
|
+
.await;
|
|
311
411
|
ancestor.write().await.attach_child(new_node.clone()).await;
|
|
312
412
|
new_node
|
|
313
413
|
}
|
|
@@ -318,9 +418,14 @@ impl MerkleTree {
|
|
|
318
418
|
// UNSURE: not sure this is the correct thing to do but it is the fastest.
|
|
319
419
|
// get the last thing that is not in the tree.
|
|
320
420
|
let first_child_path = path.last().unwrap();
|
|
321
|
-
let first_child =
|
|
322
|
-
|
|
323
|
-
|
|
421
|
+
let first_child = MerkleNode::new(
|
|
422
|
+
first_child_path.clone(),
|
|
423
|
+
Some(ancestor.clone()),
|
|
424
|
+
&self.git_ignored_files_and_dirs,
|
|
425
|
+
&absolute_root_path.as_str(),
|
|
426
|
+
self.is_git_repo
|
|
427
|
+
)
|
|
428
|
+
.await;
|
|
324
429
|
|
|
325
430
|
// TODO(sualeh): we should do an assertion check that the entire vec is contained here.
|
|
326
431
|
|
|
@@ -597,18 +702,57 @@ use std::future::Future;
|
|
|
597
702
|
use std::pin::Pin;
|
|
598
703
|
|
|
599
704
|
type PinnedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
|
705
|
+
type IgnoredFiles = HashSet<String>;
|
|
600
706
|
|
|
601
707
|
impl MerkleNode {
|
|
602
708
|
/// please be careful using this.
|
|
603
709
|
async fn __new_unchecked(
|
|
604
710
|
file_or_directory: String,
|
|
605
711
|
parent: ParentPtr,
|
|
712
|
+
ignored_files: &IgnoredFiles,
|
|
713
|
+
absolute_root_path: &str,
|
|
714
|
+
is_git_repo: bool,
|
|
606
715
|
) -> MerkleNodePtr {
|
|
607
|
-
|
|
716
|
+
// // check if the root is a git directory.
|
|
717
|
+
// let is_git_repo =
|
|
718
|
+
// match git_utils::is_git_directory(absolute_root_path).await {
|
|
719
|
+
// Ok(is_git_repo) => is_git_repo,
|
|
720
|
+
// Err(_e) => false,
|
|
721
|
+
// };
|
|
722
|
+
let bypass_git = !is_git_repo;
|
|
723
|
+
|
|
724
|
+
MerkleNode::construct_node(
|
|
725
|
+
Path::new(&file_or_directory),
|
|
726
|
+
parent,
|
|
727
|
+
ignored_files,
|
|
728
|
+
absolute_root_path,
|
|
729
|
+
bypass_git,
|
|
730
|
+
)
|
|
731
|
+
.await
|
|
608
732
|
}
|
|
609
733
|
|
|
610
|
-
async fn new(
|
|
611
|
-
|
|
734
|
+
async fn new(
|
|
735
|
+
absolute_file_or_directory: PathBuf,
|
|
736
|
+
parent: ParentPtr,
|
|
737
|
+
ignored_files: &IgnoredFiles,
|
|
738
|
+
absolute_root_path: &str,
|
|
739
|
+
is_git_repo: bool,
|
|
740
|
+
) -> MerkleNodePtr {
|
|
741
|
+
let bypass_git = !is_git_repo;
|
|
742
|
+
|
|
743
|
+
info!(
|
|
744
|
+
"constructing node for absolute_file_or_directory: {:?}",
|
|
745
|
+
absolute_file_or_directory
|
|
746
|
+
);
|
|
747
|
+
|
|
748
|
+
MerkleNode::construct_node(
|
|
749
|
+
Path::new(&absolute_file_or_directory),
|
|
750
|
+
parent,
|
|
751
|
+
ignored_files,
|
|
752
|
+
absolute_root_path,
|
|
753
|
+
bypass_git,
|
|
754
|
+
)
|
|
755
|
+
.await
|
|
612
756
|
}
|
|
613
757
|
|
|
614
758
|
/// NOT added to the tree by default.
|
|
@@ -619,38 +763,51 @@ impl MerkleNode {
|
|
|
619
763
|
// let file_hash = self.files.get_mut(&file_path).unwrap();
|
|
620
764
|
|
|
621
765
|
fn construct_node<'a>(
|
|
622
|
-
|
|
766
|
+
absolute_file_or_directory: &'a Path,
|
|
623
767
|
parent: ParentPtr,
|
|
768
|
+
ignored_files: &'a IgnoredFiles,
|
|
769
|
+
absolute_root_path: &'a str,
|
|
770
|
+
bypass_git: bool,
|
|
624
771
|
) -> PinnedFuture<'a, MerkleNodePtr> {
|
|
625
772
|
Box::pin(async move {
|
|
626
773
|
// check if it is a file
|
|
627
|
-
let path_str =
|
|
628
|
-
if
|
|
774
|
+
let path_str = absolute_file_or_directory.to_str().unwrap().to_string();
|
|
775
|
+
if absolute_file_or_directory.is_file() {
|
|
629
776
|
return Arc::new(RwLock::new(
|
|
630
777
|
MerkleNode::construct_file_node_or_error_node(
|
|
631
|
-
|
|
778
|
+
absolute_file_or_directory,
|
|
632
779
|
parent,
|
|
780
|
+
ignored_files,
|
|
633
781
|
)
|
|
634
782
|
.await,
|
|
635
783
|
));
|
|
636
784
|
}
|
|
637
785
|
|
|
638
786
|
// check if the directory fails the bad dir test.
|
|
639
|
-
let is_bad_dir = file_utils::is_in_bad_dir(
|
|
787
|
+
let is_bad_dir = file_utils::is_in_bad_dir(absolute_file_or_directory);
|
|
640
788
|
if is_bad_dir.is_err() || is_bad_dir.unwrap_or(false) {
|
|
641
789
|
// println!("skipping directory: {}", path_str);
|
|
642
790
|
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
643
|
-
Some(
|
|
791
|
+
Some(absolute_file_or_directory),
|
|
644
792
|
Some("Directory is in bad dir!".to_string()),
|
|
645
793
|
)));
|
|
646
794
|
}
|
|
647
795
|
|
|
648
|
-
let
|
|
796
|
+
let is_git_ignored_dir = ignored_files.contains(&path_str);
|
|
797
|
+
|
|
798
|
+
if is_git_ignored_dir && !bypass_git {
|
|
799
|
+
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
800
|
+
Some(absolute_file_or_directory),
|
|
801
|
+
Some("Directory is git ignored!".to_string()),
|
|
802
|
+
)));
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
let entries = fs::read_dir(absolute_file_or_directory);
|
|
649
806
|
match entries {
|
|
650
807
|
Ok(_) => (),
|
|
651
808
|
Err(e) => {
|
|
652
809
|
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
653
|
-
Some(
|
|
810
|
+
Some(absolute_file_or_directory),
|
|
654
811
|
Some(e.to_string()),
|
|
655
812
|
)));
|
|
656
813
|
}
|
|
@@ -670,13 +827,19 @@ impl MerkleNode {
|
|
|
670
827
|
match entry {
|
|
671
828
|
Ok(entry) => {
|
|
672
829
|
children.push(
|
|
673
|
-
MerkleNode::construct_node(
|
|
674
|
-
.
|
|
830
|
+
MerkleNode::construct_node(
|
|
831
|
+
&entry.path(),
|
|
832
|
+
Some(node.clone()),
|
|
833
|
+
ignored_files,
|
|
834
|
+
absolute_root_path,
|
|
835
|
+
bypass_git,
|
|
836
|
+
)
|
|
837
|
+
.await,
|
|
675
838
|
);
|
|
676
839
|
}
|
|
677
840
|
Err(e) => {
|
|
678
841
|
children.push(Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
679
|
-
Some(
|
|
842
|
+
Some(absolute_file_or_directory),
|
|
680
843
|
Some(e.to_string()),
|
|
681
844
|
))));
|
|
682
845
|
}
|
|
@@ -696,31 +859,37 @@ impl MerkleNode {
|
|
|
696
859
|
}
|
|
697
860
|
|
|
698
861
|
async fn construct_file_node(
|
|
699
|
-
|
|
862
|
+
absolute_file_path: &Path,
|
|
700
863
|
parent: ParentPtr,
|
|
864
|
+
ignored_files: &IgnoredFiles,
|
|
701
865
|
) -> Result<MerkleNode, String> {
|
|
702
|
-
let file_str =
|
|
866
|
+
let file_str = absolute_file_path
|
|
703
867
|
.to_str()
|
|
704
868
|
.ok_or("Could not convert file path to string!")?
|
|
705
869
|
.to_string();
|
|
706
870
|
// first see if it passes the
|
|
707
|
-
match file_utils::is_good_file(
|
|
871
|
+
match file_utils::is_good_file(absolute_file_path) {
|
|
708
872
|
Ok(_) => {}
|
|
709
873
|
Err(e) => {
|
|
710
874
|
return Err(format!("File failed runtime checks! {}", e.to_string()));
|
|
711
875
|
}
|
|
712
876
|
}
|
|
713
877
|
|
|
714
|
-
//
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
return Err(format!("
|
|
878
|
+
// check if the file is in the git ignore buffer.
|
|
879
|
+
// this is a bug right because we are not checking absoluteness here.
|
|
880
|
+
match ignored_files.contains(&file_str) {
|
|
881
|
+
true => {
|
|
882
|
+
return Err(format!("File is in git ignore buffer!"));
|
|
719
883
|
}
|
|
720
|
-
|
|
884
|
+
false => {}
|
|
885
|
+
}
|
|
721
886
|
|
|
722
887
|
// check if the file passes runtime checks.
|
|
723
|
-
match file_utils::is_good_file_runtime_check(
|
|
888
|
+
match file_utils::is_good_file_runtime_check(
|
|
889
|
+
absolute_file_path,
|
|
890
|
+
// &file_content,
|
|
891
|
+
)
|
|
892
|
+
.await
|
|
724
893
|
{
|
|
725
894
|
Ok(_) => {}
|
|
726
895
|
Err(e) => {
|
|
@@ -728,15 +897,14 @@ impl MerkleNode {
|
|
|
728
897
|
}
|
|
729
898
|
}
|
|
730
899
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
e.to_string()
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
};
|
|
900
|
+
// read the file_content to a buffer
|
|
901
|
+
let file_content =
|
|
902
|
+
match file_utils::read_string_without_bom(absolute_file_path).await {
|
|
903
|
+
Ok(content) => content,
|
|
904
|
+
Err(e) => {
|
|
905
|
+
return Err(format!("Could not read file! {}", e.to_string()));
|
|
906
|
+
}
|
|
907
|
+
};
|
|
740
908
|
|
|
741
909
|
let file_hash = compute_hash(&file_content);
|
|
742
910
|
let node = MerkleNode {
|
|
@@ -751,15 +919,22 @@ impl MerkleNode {
|
|
|
751
919
|
}
|
|
752
920
|
|
|
753
921
|
async fn construct_file_node_or_error_node(
|
|
754
|
-
|
|
922
|
+
absolute_file_path: &Path,
|
|
755
923
|
parent: ParentPtr,
|
|
924
|
+
ignored_files: &IgnoredFiles,
|
|
756
925
|
) -> MerkleNode {
|
|
757
|
-
let node = match MerkleNode::construct_file_node(
|
|
926
|
+
let node = match MerkleNode::construct_file_node(
|
|
927
|
+
absolute_file_path,
|
|
928
|
+
parent,
|
|
929
|
+
ignored_files,
|
|
930
|
+
)
|
|
931
|
+
.await
|
|
932
|
+
{
|
|
758
933
|
Ok(node) => node,
|
|
759
934
|
Err(e) => {
|
|
760
935
|
// println!("constructing error node. error: {}", e);
|
|
761
936
|
// println!("file_path: {:?}", file_path);
|
|
762
|
-
MerkleNode::empty_node(Some(
|
|
937
|
+
MerkleNode::empty_node(Some(absolute_file_path), Some(e))
|
|
763
938
|
}
|
|
764
939
|
};
|
|
765
940
|
|
|
@@ -785,15 +960,51 @@ impl MerkleNode {
|
|
|
785
960
|
|
|
786
961
|
async fn compute_branch_hash(children: &[MerkleNodePtr]) -> String {
|
|
787
962
|
let mut hasher = sha2::Sha256::new();
|
|
963
|
+
let mut names_and_hashes = vec![];
|
|
964
|
+
let mut non_zero_children = 0;
|
|
965
|
+
|
|
788
966
|
for child in children {
|
|
789
967
|
// check if it is an error node
|
|
790
968
|
let child_reader = child.read().await;
|
|
791
|
-
|
|
969
|
+
|
|
970
|
+
match &child_reader.node_type {
|
|
971
|
+
NodeType::File(file_name) => {
|
|
972
|
+
non_zero_children += 1;
|
|
973
|
+
names_and_hashes.push((file_name.clone(), child_reader.hash.clone()));
|
|
974
|
+
}
|
|
975
|
+
NodeType::Branch((file_name, _)) => {
|
|
976
|
+
let hash = child_reader.hash.clone();
|
|
977
|
+
if hash == "" {
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
non_zero_children += 1;
|
|
982
|
+
names_and_hashes.push((file_name.clone(), hash));
|
|
983
|
+
}
|
|
984
|
+
NodeType::ErrorNode(_) => {
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// sort the list of names and hashes by the hashes!!
|
|
991
|
+
names_and_hashes
|
|
992
|
+
.sort_by(|a, b| a.1.to_lowercase().cmp(&b.1.to_lowercase()));
|
|
993
|
+
|
|
994
|
+
for (name, hash) in names_and_hashes {
|
|
995
|
+
if hash == "" {
|
|
792
996
|
continue;
|
|
793
997
|
}
|
|
998
|
+
info!("name: {}, hash: {}", name, hash);
|
|
999
|
+
hasher.update(hash);
|
|
1000
|
+
}
|
|
794
1001
|
|
|
795
|
-
|
|
1002
|
+
if non_zero_children == 0 {
|
|
1003
|
+
// this means that the branch is empty.
|
|
1004
|
+
// we should return an empty string.
|
|
1005
|
+
return "".to_string();
|
|
796
1006
|
}
|
|
1007
|
+
|
|
797
1008
|
let result = hasher.finalize();
|
|
798
1009
|
format!("{:x}", result)
|
|
799
1010
|
}
|
package/src/merkle_tree/test.rs
CHANGED
|
@@ -43,8 +43,9 @@ mod tests {
|
|
|
43
43
|
// let path = Path::new(&temp_dir_path);
|
|
44
44
|
|
|
45
45
|
// Test construct_merkle_tree() function
|
|
46
|
+
let new_set = std::collections::HashSet::<String>::new();
|
|
46
47
|
let tree =
|
|
47
|
-
MerkleTree::construct_merkle_tree(temp_dir_path.clone()).await;
|
|
48
|
+
MerkleTree::construct_merkle_tree(temp_dir_path.clone(), new_set, false).await;
|
|
48
49
|
let mut tree = match tree {
|
|
49
50
|
Ok(tree) => {
|
|
50
51
|
assert_eq!(tree.files.len(), 2);
|