@anysphere/file-service 0.0.0-e1f2f04d → 0.0.0-e36a46ab
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 +11 -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 +148 -15
- package/src/merkle_tree/local_construction.rs +46 -9
- package/src/merkle_tree/mod.rs +332 -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,125 @@ 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
|
+
info!("get_spline called with absolute_path: {}", absolute_path);
|
|
326
|
+
let mut files = Vec::new();
|
|
261
327
|
|
|
262
|
-
|
|
328
|
+
let current_node = match self.files.get(absolute_path) {
|
|
329
|
+
Some(node) => {
|
|
330
|
+
info!("Found node for absolute_path: {}", absolute_path);
|
|
331
|
+
node.node.clone()
|
|
332
|
+
}
|
|
333
|
+
None => {
|
|
334
|
+
info!("File not found for absolute_path: {}", absolute_path);
|
|
335
|
+
return Err(anyhow::anyhow!("File not found: {}", absolute_path));
|
|
336
|
+
}
|
|
337
|
+
};
|
|
263
338
|
|
|
264
|
-
|
|
265
|
-
|
|
339
|
+
let mut stack = Vec::new();
|
|
340
|
+
stack.push(current_node);
|
|
341
|
+
|
|
342
|
+
while let Some(node) = stack.pop() {
|
|
343
|
+
let parent = node.read().await.parent.clone();
|
|
344
|
+
if let Some(parent) = parent {
|
|
345
|
+
info!("Adding parent hash to files vector");
|
|
346
|
+
{
|
|
347
|
+
let parent_node = parent.read().await;
|
|
348
|
+
match &parent_node.node_type {
|
|
349
|
+
NodeType::File(file_name) => {
|
|
350
|
+
files.push(file_name.clone());
|
|
351
|
+
}
|
|
352
|
+
NodeType::Branch((branch_name, _)) => {
|
|
353
|
+
files.push(branch_name.clone());
|
|
354
|
+
}
|
|
355
|
+
_ => {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
266
360
|
|
|
267
|
-
|
|
361
|
+
stack.push(parent);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
info!("Returning files vector with {} elements", files.len());
|
|
365
|
+
Ok(files)
|
|
268
366
|
}
|
|
269
367
|
|
|
270
368
|
/// creates a new node and attaches it to the current tree.
|
|
@@ -302,12 +400,19 @@ impl MerkleTree {
|
|
|
302
400
|
// 1. the path is empty. this means that the ancestor is the root.
|
|
303
401
|
// 2. the path is non-empty. that means there exist a non-empty element btwn till the root.
|
|
304
402
|
|
|
403
|
+
let absolute_root_path = self.root_path.clone();
|
|
305
404
|
let new_node = match path.len() {
|
|
306
405
|
0 => {
|
|
307
406
|
// this means that the ancestor is the root.
|
|
308
407
|
// we need to create a new node and attach it to the ancestor.
|
|
309
|
-
let new_node =
|
|
310
|
-
|
|
408
|
+
let new_node = MerkleNode::new(
|
|
409
|
+
file_path.clone(),
|
|
410
|
+
Some(ancestor.clone()),
|
|
411
|
+
&self.git_ignored_files_and_dirs,
|
|
412
|
+
&absolute_root_path.as_str(),
|
|
413
|
+
self.is_git_repo
|
|
414
|
+
)
|
|
415
|
+
.await;
|
|
311
416
|
ancestor.write().await.attach_child(new_node.clone()).await;
|
|
312
417
|
new_node
|
|
313
418
|
}
|
|
@@ -318,9 +423,14 @@ impl MerkleTree {
|
|
|
318
423
|
// UNSURE: not sure this is the correct thing to do but it is the fastest.
|
|
319
424
|
// get the last thing that is not in the tree.
|
|
320
425
|
let first_child_path = path.last().unwrap();
|
|
321
|
-
let first_child =
|
|
322
|
-
|
|
323
|
-
|
|
426
|
+
let first_child = MerkleNode::new(
|
|
427
|
+
first_child_path.clone(),
|
|
428
|
+
Some(ancestor.clone()),
|
|
429
|
+
&self.git_ignored_files_and_dirs,
|
|
430
|
+
&absolute_root_path.as_str(),
|
|
431
|
+
self.is_git_repo
|
|
432
|
+
)
|
|
433
|
+
.await;
|
|
324
434
|
|
|
325
435
|
// TODO(sualeh): we should do an assertion check that the entire vec is contained here.
|
|
326
436
|
|
|
@@ -597,18 +707,58 @@ use std::future::Future;
|
|
|
597
707
|
use std::pin::Pin;
|
|
598
708
|
|
|
599
709
|
type PinnedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
|
710
|
+
type IgnoredFiles = HashSet<String>;
|
|
600
711
|
|
|
601
712
|
impl MerkleNode {
|
|
602
713
|
/// please be careful using this.
|
|
603
714
|
async fn __new_unchecked(
|
|
604
715
|
file_or_directory: String,
|
|
605
716
|
parent: ParentPtr,
|
|
717
|
+
ignored_files: &IgnoredFiles,
|
|
718
|
+
absolute_root_path: &str,
|
|
719
|
+
is_git_repo: bool,
|
|
606
720
|
) -> MerkleNodePtr {
|
|
607
|
-
|
|
721
|
+
// // check if the root is a git directory.
|
|
722
|
+
// let is_git_repo =
|
|
723
|
+
// match git_utils::is_git_directory(absolute_root_path).await {
|
|
724
|
+
// Ok(is_git_repo) => is_git_repo,
|
|
725
|
+
// Err(_e) => false,
|
|
726
|
+
// };
|
|
727
|
+
let bypass_git = !is_git_repo;
|
|
728
|
+
|
|
729
|
+
MerkleNode::construct_node(
|
|
730
|
+
Path::new(&file_or_directory),
|
|
731
|
+
parent,
|
|
732
|
+
ignored_files,
|
|
733
|
+
absolute_root_path,
|
|
734
|
+
bypass_git,
|
|
735
|
+
)
|
|
736
|
+
.await
|
|
608
737
|
}
|
|
609
738
|
|
|
610
|
-
async fn new(
|
|
611
|
-
|
|
739
|
+
async fn new(
|
|
740
|
+
absolute_file_or_directory: PathBuf,
|
|
741
|
+
parent: ParentPtr,
|
|
742
|
+
ignored_files: &IgnoredFiles,
|
|
743
|
+
absolute_root_path: &str,
|
|
744
|
+
is_git_repo: bool,
|
|
745
|
+
) -> MerkleNodePtr {
|
|
746
|
+
let bypass_git = !is_git_repo;
|
|
747
|
+
|
|
748
|
+
info!(
|
|
749
|
+
"constructing node for absolute_file_or_directory: {:?}",
|
|
750
|
+
absolute_file_or_directory
|
|
751
|
+
);
|
|
752
|
+
info!("bypass_git: {}, is_git_repo: {}", bypass_git, is_git_repo);
|
|
753
|
+
|
|
754
|
+
MerkleNode::construct_node(
|
|
755
|
+
Path::new(&absolute_file_or_directory),
|
|
756
|
+
parent,
|
|
757
|
+
ignored_files,
|
|
758
|
+
absolute_root_path,
|
|
759
|
+
bypass_git,
|
|
760
|
+
)
|
|
761
|
+
.await
|
|
612
762
|
}
|
|
613
763
|
|
|
614
764
|
/// NOT added to the tree by default.
|
|
@@ -619,38 +769,51 @@ impl MerkleNode {
|
|
|
619
769
|
// let file_hash = self.files.get_mut(&file_path).unwrap();
|
|
620
770
|
|
|
621
771
|
fn construct_node<'a>(
|
|
622
|
-
|
|
772
|
+
absolute_file_or_directory: &'a Path,
|
|
623
773
|
parent: ParentPtr,
|
|
774
|
+
ignored_files: &'a IgnoredFiles,
|
|
775
|
+
absolute_root_path: &'a str,
|
|
776
|
+
bypass_git: bool,
|
|
624
777
|
) -> PinnedFuture<'a, MerkleNodePtr> {
|
|
625
778
|
Box::pin(async move {
|
|
626
779
|
// check if it is a file
|
|
627
|
-
let path_str =
|
|
628
|
-
if
|
|
780
|
+
let path_str = absolute_file_or_directory.to_str().unwrap().to_string();
|
|
781
|
+
if absolute_file_or_directory.is_file() {
|
|
629
782
|
return Arc::new(RwLock::new(
|
|
630
783
|
MerkleNode::construct_file_node_or_error_node(
|
|
631
|
-
|
|
784
|
+
absolute_file_or_directory,
|
|
632
785
|
parent,
|
|
786
|
+
ignored_files,
|
|
633
787
|
)
|
|
634
788
|
.await,
|
|
635
789
|
));
|
|
636
790
|
}
|
|
637
791
|
|
|
638
792
|
// check if the directory fails the bad dir test.
|
|
639
|
-
let is_bad_dir = file_utils::is_in_bad_dir(
|
|
793
|
+
let is_bad_dir = file_utils::is_in_bad_dir(absolute_file_or_directory);
|
|
640
794
|
if is_bad_dir.is_err() || is_bad_dir.unwrap_or(false) {
|
|
641
795
|
// println!("skipping directory: {}", path_str);
|
|
642
796
|
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
643
|
-
Some(
|
|
797
|
+
Some(absolute_file_or_directory),
|
|
644
798
|
Some("Directory is in bad dir!".to_string()),
|
|
645
799
|
)));
|
|
646
800
|
}
|
|
647
801
|
|
|
648
|
-
let
|
|
802
|
+
let is_git_ignored_dir = ignored_files.contains(&path_str);
|
|
803
|
+
|
|
804
|
+
if is_git_ignored_dir && !bypass_git {
|
|
805
|
+
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
806
|
+
Some(absolute_file_or_directory),
|
|
807
|
+
Some("Directory is git ignored!".to_string()),
|
|
808
|
+
)));
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
let entries = fs::read_dir(absolute_file_or_directory);
|
|
649
812
|
match entries {
|
|
650
813
|
Ok(_) => (),
|
|
651
814
|
Err(e) => {
|
|
652
815
|
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
653
|
-
Some(
|
|
816
|
+
Some(absolute_file_or_directory),
|
|
654
817
|
Some(e.to_string()),
|
|
655
818
|
)));
|
|
656
819
|
}
|
|
@@ -670,13 +833,19 @@ impl MerkleNode {
|
|
|
670
833
|
match entry {
|
|
671
834
|
Ok(entry) => {
|
|
672
835
|
children.push(
|
|
673
|
-
MerkleNode::construct_node(
|
|
674
|
-
.
|
|
836
|
+
MerkleNode::construct_node(
|
|
837
|
+
&entry.path(),
|
|
838
|
+
Some(node.clone()),
|
|
839
|
+
ignored_files,
|
|
840
|
+
absolute_root_path,
|
|
841
|
+
bypass_git,
|
|
842
|
+
)
|
|
843
|
+
.await,
|
|
675
844
|
);
|
|
676
845
|
}
|
|
677
846
|
Err(e) => {
|
|
678
847
|
children.push(Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
679
|
-
Some(
|
|
848
|
+
Some(absolute_file_or_directory),
|
|
680
849
|
Some(e.to_string()),
|
|
681
850
|
))));
|
|
682
851
|
}
|
|
@@ -696,31 +865,37 @@ impl MerkleNode {
|
|
|
696
865
|
}
|
|
697
866
|
|
|
698
867
|
async fn construct_file_node(
|
|
699
|
-
|
|
868
|
+
absolute_file_path: &Path,
|
|
700
869
|
parent: ParentPtr,
|
|
870
|
+
ignored_files: &IgnoredFiles,
|
|
701
871
|
) -> Result<MerkleNode, String> {
|
|
702
|
-
let file_str =
|
|
872
|
+
let file_str = absolute_file_path
|
|
703
873
|
.to_str()
|
|
704
874
|
.ok_or("Could not convert file path to string!")?
|
|
705
875
|
.to_string();
|
|
706
876
|
// first see if it passes the
|
|
707
|
-
match file_utils::is_good_file(
|
|
877
|
+
match file_utils::is_good_file(absolute_file_path) {
|
|
708
878
|
Ok(_) => {}
|
|
709
879
|
Err(e) => {
|
|
710
880
|
return Err(format!("File failed runtime checks! {}", e.to_string()));
|
|
711
881
|
}
|
|
712
882
|
}
|
|
713
883
|
|
|
714
|
-
//
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
return Err(format!("
|
|
884
|
+
// check if the file is in the git ignore buffer.
|
|
885
|
+
// this is a bug right because we are not checking absoluteness here.
|
|
886
|
+
match ignored_files.contains(&file_str) {
|
|
887
|
+
true => {
|
|
888
|
+
return Err(format!("File is in git ignore buffer!"));
|
|
719
889
|
}
|
|
720
|
-
|
|
890
|
+
false => {}
|
|
891
|
+
}
|
|
721
892
|
|
|
722
893
|
// check if the file passes runtime checks.
|
|
723
|
-
match file_utils::is_good_file_runtime_check(
|
|
894
|
+
match file_utils::is_good_file_runtime_check(
|
|
895
|
+
absolute_file_path,
|
|
896
|
+
// &file_content,
|
|
897
|
+
)
|
|
898
|
+
.await
|
|
724
899
|
{
|
|
725
900
|
Ok(_) => {}
|
|
726
901
|
Err(e) => {
|
|
@@ -728,15 +903,14 @@ impl MerkleNode {
|
|
|
728
903
|
}
|
|
729
904
|
}
|
|
730
905
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
e.to_string()
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
};
|
|
906
|
+
// read the file_content to a buffer
|
|
907
|
+
let file_content =
|
|
908
|
+
match file_utils::read_string_without_bom(absolute_file_path).await {
|
|
909
|
+
Ok(content) => content,
|
|
910
|
+
Err(e) => {
|
|
911
|
+
return Err(format!("Could not read file! {}", e.to_string()));
|
|
912
|
+
}
|
|
913
|
+
};
|
|
740
914
|
|
|
741
915
|
let file_hash = compute_hash(&file_content);
|
|
742
916
|
let node = MerkleNode {
|
|
@@ -751,15 +925,22 @@ impl MerkleNode {
|
|
|
751
925
|
}
|
|
752
926
|
|
|
753
927
|
async fn construct_file_node_or_error_node(
|
|
754
|
-
|
|
928
|
+
absolute_file_path: &Path,
|
|
755
929
|
parent: ParentPtr,
|
|
930
|
+
ignored_files: &IgnoredFiles,
|
|
756
931
|
) -> MerkleNode {
|
|
757
|
-
let node = match MerkleNode::construct_file_node(
|
|
932
|
+
let node = match MerkleNode::construct_file_node(
|
|
933
|
+
absolute_file_path,
|
|
934
|
+
parent,
|
|
935
|
+
ignored_files,
|
|
936
|
+
)
|
|
937
|
+
.await
|
|
938
|
+
{
|
|
758
939
|
Ok(node) => node,
|
|
759
940
|
Err(e) => {
|
|
760
941
|
// println!("constructing error node. error: {}", e);
|
|
761
942
|
// println!("file_path: {:?}", file_path);
|
|
762
|
-
MerkleNode::empty_node(Some(
|
|
943
|
+
MerkleNode::empty_node(Some(absolute_file_path), Some(e))
|
|
763
944
|
}
|
|
764
945
|
};
|
|
765
946
|
|
|
@@ -785,15 +966,51 @@ impl MerkleNode {
|
|
|
785
966
|
|
|
786
967
|
async fn compute_branch_hash(children: &[MerkleNodePtr]) -> String {
|
|
787
968
|
let mut hasher = sha2::Sha256::new();
|
|
969
|
+
let mut names_and_hashes = vec![];
|
|
970
|
+
let mut non_zero_children = 0;
|
|
971
|
+
|
|
788
972
|
for child in children {
|
|
789
973
|
// check if it is an error node
|
|
790
974
|
let child_reader = child.read().await;
|
|
791
|
-
|
|
975
|
+
|
|
976
|
+
match &child_reader.node_type {
|
|
977
|
+
NodeType::File(file_name) => {
|
|
978
|
+
non_zero_children += 1;
|
|
979
|
+
names_and_hashes.push((file_name.clone(), child_reader.hash.clone()));
|
|
980
|
+
}
|
|
981
|
+
NodeType::Branch((file_name, _)) => {
|
|
982
|
+
let hash = child_reader.hash.clone();
|
|
983
|
+
if hash == "" {
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
non_zero_children += 1;
|
|
988
|
+
names_and_hashes.push((file_name.clone(), hash));
|
|
989
|
+
}
|
|
990
|
+
NodeType::ErrorNode(_) => {
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// sort the list of names and hashes by the hashes!!
|
|
997
|
+
names_and_hashes
|
|
998
|
+
.sort_by(|a, b| a.1.to_lowercase().cmp(&b.1.to_lowercase()));
|
|
999
|
+
|
|
1000
|
+
for (name, hash) in names_and_hashes {
|
|
1001
|
+
if hash == "" {
|
|
792
1002
|
continue;
|
|
793
1003
|
}
|
|
1004
|
+
info!("name: {}, hash: {}", name, hash);
|
|
1005
|
+
hasher.update(hash);
|
|
1006
|
+
}
|
|
794
1007
|
|
|
795
|
-
|
|
1008
|
+
if non_zero_children == 0 {
|
|
1009
|
+
// this means that the branch is empty.
|
|
1010
|
+
// we should return an empty string.
|
|
1011
|
+
return "".to_string();
|
|
796
1012
|
}
|
|
1013
|
+
|
|
797
1014
|
let result = hasher.finalize();
|
|
798
1015
|
format!("{:x}", result)
|
|
799
1016
|
}
|
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);
|