@anysphere/file-service 0.0.0-dacf38fa → 0.0.0-e0c70bcd
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 +4 -0
- package/index.d.ts +6 -1
- package/package.json +10 -8
- package/src/file_utils.rs +13 -15
- package/src/git_utils.rs +158 -19
- package/src/lib.rs +185 -56
- package/src/merkle_tree/local_construction.rs +47 -21
- package/src/merkle_tree/mod.rs +450 -148
- package/src/test.rs +5 -0
package/src/merkle_tree/mod.rs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
use crate::git_utils;
|
|
2
|
+
|
|
1
3
|
use super::file_utils;
|
|
2
4
|
use sha2::Digest;
|
|
5
|
+
use std::collections::{BTreeMap, HashSet};
|
|
3
6
|
use std::path::PathBuf;
|
|
4
|
-
use std::{
|
|
7
|
+
use std::{fs, path::Path, sync::Arc};
|
|
5
8
|
use tokio::sync::RwLock;
|
|
6
9
|
use tonic::async_trait;
|
|
10
|
+
use tracing::info;
|
|
7
11
|
pub mod local_construction;
|
|
8
12
|
pub mod test;
|
|
9
13
|
|
|
@@ -12,7 +16,9 @@ pub type MerkleNodePtr = Arc<RwLock<MerkleNode>>;
|
|
|
12
16
|
pub struct MerkleTree {
|
|
13
17
|
root_path: String,
|
|
14
18
|
root: MerkleNodePtr,
|
|
15
|
-
files:
|
|
19
|
+
files: BTreeMap<String, File>,
|
|
20
|
+
cursor: Option<usize>,
|
|
21
|
+
git_ignored_files: HashSet<String>,
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
#[derive(Debug)]
|
|
@@ -50,12 +56,19 @@ fn get_id() -> i32 {
|
|
|
50
56
|
|
|
51
57
|
#[async_trait]
|
|
52
58
|
pub trait LocalConstruction {
|
|
53
|
-
async fn new(
|
|
54
|
-
|
|
59
|
+
async fn new(
|
|
60
|
+
root_directory: Option<String>,
|
|
61
|
+
) -> Result<MerkleTree, anyhow::Error>;
|
|
62
|
+
|
|
63
|
+
async fn construct_merkle_tree(
|
|
64
|
+
root_directory: String,
|
|
65
|
+
) -> Result<MerkleTree, anyhow::Error>;
|
|
66
|
+
|
|
55
67
|
async fn update_file(
|
|
56
68
|
&mut self,
|
|
57
69
|
file_path: String,
|
|
58
70
|
) -> Result<(), anyhow::Error>;
|
|
71
|
+
|
|
59
72
|
async fn delete_file(
|
|
60
73
|
&mut self,
|
|
61
74
|
file_path: String,
|
|
@@ -79,96 +92,268 @@ impl MerkleTree {
|
|
|
79
92
|
pub fn empty_tree() -> MerkleTree {
|
|
80
93
|
MerkleTree {
|
|
81
94
|
root: Arc::new(RwLock::new(MerkleNode::empty_node(None, None))),
|
|
82
|
-
files:
|
|
95
|
+
files: BTreeMap::new(),
|
|
83
96
|
root_path: "".to_string(),
|
|
97
|
+
cursor: None,
|
|
98
|
+
git_ignored_files: HashSet::new(),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pub async fn get_subtree_hash(
|
|
103
|
+
&self,
|
|
104
|
+
absolute_path: PathBuf,
|
|
105
|
+
) -> Result<String, anyhow::Error> {
|
|
106
|
+
let abs_string = match absolute_path.to_str() {
|
|
107
|
+
Some(s) => s.to_string(),
|
|
108
|
+
None => {
|
|
109
|
+
return Err(anyhow::anyhow!(
|
|
110
|
+
"get_subtree_hash: Failed to convert path to string"
|
|
111
|
+
))
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
let node = match self.files.get(&abs_string) {
|
|
116
|
+
Some(file) => file.node.clone(),
|
|
117
|
+
None => {
|
|
118
|
+
let all_files: Vec<String> = self.files.keys().cloned().collect();
|
|
119
|
+
return Err(anyhow::anyhow!("Could not find file in tree! Looking for: {}. All files: {:?}", abs_string, all_files));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
let node_reader = node.read().await;
|
|
124
|
+
let node_hash = node_reader.hash.clone();
|
|
125
|
+
|
|
126
|
+
Ok(node_hash)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pub async fn get_num_embeddable_files(&self) -> Result<i32, anyhow::Error> {
|
|
130
|
+
let mut count = 0;
|
|
131
|
+
|
|
132
|
+
for (_, file) in &self.files {
|
|
133
|
+
let file_reader = file.node.read().await;
|
|
134
|
+
match &file_reader.node_type {
|
|
135
|
+
NodeType::File(_) => {
|
|
136
|
+
count += 1;
|
|
137
|
+
}
|
|
138
|
+
NodeType::Branch(_) => {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
NodeType::ErrorNode(_) => {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
Ok(count)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pub async fn get_num_embeddable_files_in_subtree(
|
|
151
|
+
&self,
|
|
152
|
+
absolute_path: PathBuf,
|
|
153
|
+
) -> Result<i32, anyhow::Error> {
|
|
154
|
+
let mut count = 0;
|
|
155
|
+
|
|
156
|
+
let absolute_path = match absolute_path.to_str() {
|
|
157
|
+
Some(s) => s.to_string(),
|
|
158
|
+
None => {
|
|
159
|
+
return Err(anyhow::anyhow!(
|
|
160
|
+
"get_num_embeddable_files_in_subtree: Failed to convert path to string"
|
|
161
|
+
))
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// TODO(sualeh): worth keeping this list sorted. its now a btree
|
|
166
|
+
|
|
167
|
+
for (_, file) in &self.files {
|
|
168
|
+
let file_reader = file.node.read().await;
|
|
169
|
+
match &file_reader.node_type {
|
|
170
|
+
NodeType::File(file_name) => {
|
|
171
|
+
if file_name.contains(&absolute_path) {
|
|
172
|
+
count += 1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
NodeType::Branch(_) => {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
NodeType::ErrorNode(_) => {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
Ok(count)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
pub async fn get_all_files(&self) -> Result<Vec<String>, anyhow::Error> {
|
|
188
|
+
let mut files = Vec::new();
|
|
189
|
+
|
|
190
|
+
for (file_name, file) in &self.files {
|
|
191
|
+
let file_reader = file.node.read().await;
|
|
192
|
+
match &file_reader.node_type {
|
|
193
|
+
NodeType::File(_) => {
|
|
194
|
+
files.push(file_name.clone());
|
|
195
|
+
}
|
|
196
|
+
NodeType::Branch(_) => {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
NodeType::ErrorNode(_) => {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
Ok(files)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
pub async fn get_hashes_for_files(
|
|
209
|
+
&self,
|
|
210
|
+
files: Vec<String>,
|
|
211
|
+
) -> Result<Vec<String>, anyhow::Error> {
|
|
212
|
+
let mut hashes = Vec::new();
|
|
213
|
+
|
|
214
|
+
for file_name in files {
|
|
215
|
+
let file = match self.files.get(&file_name) {
|
|
216
|
+
Some(file) => file,
|
|
217
|
+
None => {
|
|
218
|
+
return Err(anyhow::anyhow!("Could not find file in tree!"));
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
let file_reader = file.node.read().await;
|
|
223
|
+
match &file_reader.node_type {
|
|
224
|
+
NodeType::File(_) => {
|
|
225
|
+
hashes.push(file_reader.hash.clone());
|
|
226
|
+
}
|
|
227
|
+
NodeType::Branch(_) => {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
NodeType::ErrorNode(_) => {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
Ok(hashes)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/// Returns a filename, and then a path from its parent to the root (which can possibly be empty.)
|
|
240
|
+
pub async fn get_next_file_to_embed(
|
|
241
|
+
&mut self,
|
|
242
|
+
) -> Result<(String, Vec<String>), anyhow::Error> {
|
|
243
|
+
// if the cursor is none, set it to 0
|
|
244
|
+
let cursor = match self.cursor {
|
|
245
|
+
Some(cursor) => cursor,
|
|
246
|
+
None => {
|
|
247
|
+
self.cursor = Some(0);
|
|
248
|
+
0
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// get the thing at the cursor. while we dont find a file, we keep incrementing the cursor.
|
|
253
|
+
let mut cursor = cursor;
|
|
254
|
+
loop {
|
|
255
|
+
// O(log n)
|
|
256
|
+
let file = match self.files.values().nth(cursor) {
|
|
257
|
+
Some(file) => file,
|
|
258
|
+
None => {
|
|
259
|
+
return Err(anyhow::anyhow!("Could not find file to embed!"));
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
let file_reader = file.node.read().await;
|
|
264
|
+
match &file_reader.node_type {
|
|
265
|
+
NodeType::File(f) => {
|
|
266
|
+
// update the cursor.
|
|
267
|
+
self.cursor = Some(cursor + 1);
|
|
268
|
+
let spline = self.get_spline(f).await?;
|
|
269
|
+
return Ok((f.clone(), spline));
|
|
270
|
+
}
|
|
271
|
+
NodeType::Branch(_) => {
|
|
272
|
+
cursor += 1;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
NodeType::ErrorNode(_) => {
|
|
276
|
+
cursor += 1;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
pub async fn get_all_dir_files_to_embed(
|
|
284
|
+
&self,
|
|
285
|
+
absolute_path: &str,
|
|
286
|
+
) -> Result<Vec<String>, anyhow::Error> {
|
|
287
|
+
let mut files = Vec::new();
|
|
288
|
+
|
|
289
|
+
for (file_path, f) in &self.files {
|
|
290
|
+
if !file_path.contains(absolute_path) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
match f.node.read().await.node_type {
|
|
295
|
+
NodeType::File(_) => {
|
|
296
|
+
files.push(file_path.clone());
|
|
297
|
+
}
|
|
298
|
+
NodeType::Branch(_) => {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
NodeType::ErrorNode(_) => {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
84
305
|
}
|
|
306
|
+
|
|
307
|
+
Ok(files)
|
|
85
308
|
}
|
|
86
309
|
|
|
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
|
-
let file_reader = file.node.read().await;
|
|
128
|
-
match &file_reader.node_type {
|
|
129
|
-
NodeType::File(_) => {
|
|
130
|
-
files.push(file_name.clone());
|
|
131
|
-
}
|
|
132
|
-
NodeType::Branch(_) => {
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
NodeType::ErrorNode(_) => {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
Ok(files)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
pub async fn get_hashes_for_files(&self, files: Vec<String>) -> Result<Vec<String>, anyhow::Error> {
|
|
145
|
-
let mut hashes = Vec::new();
|
|
146
|
-
|
|
147
|
-
for file_name in files {
|
|
148
|
-
let file = match self.files.get(&file_name) {
|
|
149
|
-
Some(file) => file,
|
|
150
|
-
None => {
|
|
151
|
-
return Err(anyhow::anyhow!("Could not find file in tree!"));
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
let file_reader = file.node.read().await;
|
|
156
|
-
match &file_reader.node_type {
|
|
157
|
-
NodeType::File(_) => {
|
|
158
|
-
hashes.push(file_reader.hash.clone());
|
|
159
|
-
}
|
|
160
|
-
NodeType::Branch(_) => {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
NodeType::ErrorNode(_) => {
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
Ok(hashes)
|
|
170
|
-
}
|
|
310
|
+
// TODO(sualeh): i need tests for this!!
|
|
311
|
+
pub async fn get_spline(
|
|
312
|
+
&self,
|
|
313
|
+
absolute_path: &str,
|
|
314
|
+
) -> Result<Vec<String>, anyhow::Error> {
|
|
315
|
+
info!("get_spline called with absolute_path: {}", absolute_path);
|
|
316
|
+
let mut files = Vec::new();
|
|
317
|
+
|
|
318
|
+
let current_node = match self.files.get(absolute_path) {
|
|
319
|
+
Some(node) => {
|
|
320
|
+
info!("Found node for absolute_path: {}", absolute_path);
|
|
321
|
+
node.node.clone()
|
|
322
|
+
}
|
|
323
|
+
None => {
|
|
324
|
+
info!("File not found for absolute_path: {}", absolute_path);
|
|
325
|
+
return Err(anyhow::anyhow!("File not found: {}", absolute_path));
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
let mut stack = Vec::new();
|
|
330
|
+
stack.push(current_node);
|
|
331
|
+
|
|
332
|
+
while let Some(node) = stack.pop() {
|
|
333
|
+
let parent = node.read().await.parent.clone();
|
|
334
|
+
if let Some(parent) = parent {
|
|
335
|
+
info!("Adding parent hash to files vector");
|
|
336
|
+
{
|
|
337
|
+
let parent_node = parent.read().await;
|
|
338
|
+
match &parent_node.node_type {
|
|
339
|
+
NodeType::File(file_name) => {
|
|
340
|
+
files.push(file_name.clone());
|
|
341
|
+
}
|
|
342
|
+
NodeType::Branch((branch_name, _)) => {
|
|
343
|
+
files.push(branch_name.clone());
|
|
344
|
+
}
|
|
345
|
+
_ => {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
171
350
|
|
|
351
|
+
stack.push(parent);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
info!("Returning files vector with {} elements", files.len());
|
|
355
|
+
Ok(files)
|
|
356
|
+
}
|
|
172
357
|
|
|
173
358
|
/// creates a new node and attaches it to the current tree.
|
|
174
359
|
/// SPEC:
|
|
@@ -205,12 +390,18 @@ impl MerkleTree {
|
|
|
205
390
|
// 1. the path is empty. this means that the ancestor is the root.
|
|
206
391
|
// 2. the path is non-empty. that means there exist a non-empty element btwn till the root.
|
|
207
392
|
|
|
393
|
+
let absolute_root_path = self.root_path.clone();
|
|
208
394
|
let new_node = match path.len() {
|
|
209
395
|
0 => {
|
|
210
396
|
// this means that the ancestor is the root.
|
|
211
397
|
// we need to create a new node and attach it to the ancestor.
|
|
212
|
-
let new_node =
|
|
213
|
-
|
|
398
|
+
let new_node = MerkleNode::new(
|
|
399
|
+
file_path.clone(),
|
|
400
|
+
Some(ancestor.clone()),
|
|
401
|
+
&self.git_ignored_files,
|
|
402
|
+
&absolute_root_path.as_str(),
|
|
403
|
+
)
|
|
404
|
+
.await;
|
|
214
405
|
ancestor.write().await.attach_child(new_node.clone()).await;
|
|
215
406
|
new_node
|
|
216
407
|
}
|
|
@@ -221,9 +412,13 @@ impl MerkleTree {
|
|
|
221
412
|
// UNSURE: not sure this is the correct thing to do but it is the fastest.
|
|
222
413
|
// get the last thing that is not in the tree.
|
|
223
414
|
let first_child_path = path.last().unwrap();
|
|
224
|
-
let first_child =
|
|
225
|
-
|
|
226
|
-
|
|
415
|
+
let first_child = MerkleNode::new(
|
|
416
|
+
first_child_path.clone(),
|
|
417
|
+
Some(ancestor.clone()),
|
|
418
|
+
&self.git_ignored_files,
|
|
419
|
+
&absolute_root_path.as_str(),
|
|
420
|
+
)
|
|
421
|
+
.await;
|
|
227
422
|
|
|
228
423
|
// TODO(sualeh): we should do an assertion check that the entire vec is contained here.
|
|
229
424
|
|
|
@@ -234,7 +429,7 @@ impl MerkleTree {
|
|
|
234
429
|
}
|
|
235
430
|
};
|
|
236
431
|
|
|
237
|
-
|
|
432
|
+
Ok(new_node)
|
|
238
433
|
}
|
|
239
434
|
|
|
240
435
|
/// Spec:
|
|
@@ -298,17 +493,21 @@ impl MerkleTree {
|
|
|
298
493
|
/// - attaches to the ancestor.
|
|
299
494
|
/// - adds to the filemap
|
|
300
495
|
/// - updates hashes of ancestor path.
|
|
301
|
-
async fn attach_new_node_to_tree(
|
|
496
|
+
async fn attach_new_node_to_tree(
|
|
497
|
+
&mut self,
|
|
498
|
+
file_path: String,
|
|
499
|
+
) -> Result<(), anyhow::Error> {
|
|
302
500
|
let path = PathBuf::from(file_path.clone());
|
|
303
501
|
match self.create_new_node_and_attach_to_ancestors(path).await {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
502
|
+
Ok(node_ptr) => {
|
|
503
|
+
self.add_subtree_to_filemap(node_ptr).await;
|
|
504
|
+
Ok(())
|
|
505
|
+
}
|
|
506
|
+
Err(e) => Err(anyhow::anyhow!(
|
|
507
|
+
"Could not create new node and attach to ancestors! {}",
|
|
508
|
+
e.to_string()
|
|
509
|
+
)),
|
|
510
|
+
}
|
|
312
511
|
}
|
|
313
512
|
|
|
314
513
|
/// MUTATES MUTATES MUTATES
|
|
@@ -389,20 +588,22 @@ impl MerkleTree {
|
|
|
389
588
|
.create_new_node_and_attach_to_ancestors(file_path)
|
|
390
589
|
.await;
|
|
391
590
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
591
|
+
match node_ptr {
|
|
592
|
+
Ok(node_ptr) => {
|
|
593
|
+
self.files.insert(
|
|
594
|
+
file_string,
|
|
595
|
+
File {
|
|
596
|
+
node: node_ptr.clone(),
|
|
597
|
+
},
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
Err(e) => {
|
|
601
|
+
return Err(anyhow::anyhow!(
|
|
602
|
+
"Could not create new node and attach to ancestors! {}",
|
|
603
|
+
e.to_string()
|
|
604
|
+
));
|
|
605
|
+
}
|
|
606
|
+
}
|
|
406
607
|
|
|
407
608
|
Ok(())
|
|
408
609
|
}
|
|
@@ -494,18 +695,62 @@ use std::future::Future;
|
|
|
494
695
|
use std::pin::Pin;
|
|
495
696
|
|
|
496
697
|
type PinnedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
|
698
|
+
type IgnoredFiles = HashSet<String>;
|
|
497
699
|
|
|
498
700
|
impl MerkleNode {
|
|
499
701
|
/// please be careful using this.
|
|
500
702
|
async fn __new_unchecked(
|
|
501
703
|
file_or_directory: String,
|
|
502
704
|
parent: ParentPtr,
|
|
705
|
+
ignored_files: &IgnoredFiles,
|
|
706
|
+
absolute_root_path: &str,
|
|
503
707
|
) -> MerkleNodePtr {
|
|
504
|
-
|
|
708
|
+
// check if the root is a git directory.
|
|
709
|
+
let is_git_repo =
|
|
710
|
+
match git_utils::is_git_directory(absolute_root_path).await {
|
|
711
|
+
Ok(is_git_repo) => is_git_repo,
|
|
712
|
+
Err(e) => false,
|
|
713
|
+
};
|
|
714
|
+
let bypass_git = !is_git_repo;
|
|
715
|
+
|
|
716
|
+
MerkleNode::construct_node(
|
|
717
|
+
Path::new(&file_or_directory),
|
|
718
|
+
parent,
|
|
719
|
+
ignored_files,
|
|
720
|
+
absolute_root_path,
|
|
721
|
+
bypass_git,
|
|
722
|
+
)
|
|
723
|
+
.await
|
|
505
724
|
}
|
|
506
725
|
|
|
507
|
-
async fn new(
|
|
508
|
-
|
|
726
|
+
async fn new(
|
|
727
|
+
absolute_file_or_directory: PathBuf,
|
|
728
|
+
parent: ParentPtr,
|
|
729
|
+
ignored_files: &IgnoredFiles,
|
|
730
|
+
absolute_root_path: &str,
|
|
731
|
+
) -> MerkleNodePtr {
|
|
732
|
+
// check if the root is a git directory.
|
|
733
|
+
let is_git_repo =
|
|
734
|
+
match git_utils::is_git_directory(absolute_root_path).await {
|
|
735
|
+
Ok(is_git_repo) => is_git_repo,
|
|
736
|
+
Err(_e) => false,
|
|
737
|
+
};
|
|
738
|
+
let bypass_git = !is_git_repo;
|
|
739
|
+
|
|
740
|
+
info!(
|
|
741
|
+
"constructing node for absolute_file_or_directory: {:?}",
|
|
742
|
+
absolute_file_or_directory
|
|
743
|
+
);
|
|
744
|
+
info!("bypass_git: {}, is_git_repo: {}", bypass_git, is_git_repo);
|
|
745
|
+
|
|
746
|
+
MerkleNode::construct_node(
|
|
747
|
+
Path::new(&absolute_file_or_directory),
|
|
748
|
+
parent,
|
|
749
|
+
ignored_files,
|
|
750
|
+
absolute_root_path,
|
|
751
|
+
bypass_git,
|
|
752
|
+
)
|
|
753
|
+
.await
|
|
509
754
|
}
|
|
510
755
|
|
|
511
756
|
/// NOT added to the tree by default.
|
|
@@ -516,38 +761,68 @@ impl MerkleNode {
|
|
|
516
761
|
// let file_hash = self.files.get_mut(&file_path).unwrap();
|
|
517
762
|
|
|
518
763
|
fn construct_node<'a>(
|
|
519
|
-
|
|
764
|
+
absolute_file_or_directory: &'a Path,
|
|
520
765
|
parent: ParentPtr,
|
|
766
|
+
ignored_files: &'a IgnoredFiles,
|
|
767
|
+
absolute_root_path: &'a str,
|
|
768
|
+
bypass_git: bool,
|
|
521
769
|
) -> PinnedFuture<'a, MerkleNodePtr> {
|
|
522
770
|
Box::pin(async move {
|
|
523
771
|
// check if it is a file
|
|
524
|
-
let path_str =
|
|
525
|
-
if
|
|
772
|
+
let path_str = absolute_file_or_directory.to_str().unwrap().to_string();
|
|
773
|
+
if absolute_file_or_directory.is_file() {
|
|
526
774
|
return Arc::new(RwLock::new(
|
|
527
775
|
MerkleNode::construct_file_node_or_error_node(
|
|
528
|
-
|
|
776
|
+
absolute_file_or_directory,
|
|
529
777
|
parent,
|
|
778
|
+
ignored_files,
|
|
530
779
|
)
|
|
531
780
|
.await,
|
|
532
781
|
));
|
|
533
782
|
}
|
|
534
783
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
784
|
+
// check if the directory fails the bad dir test.
|
|
785
|
+
let is_bad_dir = file_utils::is_in_bad_dir(absolute_file_or_directory);
|
|
786
|
+
if is_bad_dir.is_err() || is_bad_dir.unwrap_or(false) {
|
|
787
|
+
// println!("skipping directory: {}", path_str);
|
|
788
|
+
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
789
|
+
Some(absolute_file_or_directory),
|
|
790
|
+
Some("Directory is in bad dir!".to_string()),
|
|
791
|
+
)));
|
|
792
|
+
}
|
|
544
793
|
|
|
545
|
-
|
|
794
|
+
// check if the directory is git ignored
|
|
795
|
+
let is_git_ignored =
|
|
796
|
+
match git_utils::is_git_ignored(absolute_root_path, path_str.as_str())
|
|
797
|
+
.await
|
|
798
|
+
{
|
|
799
|
+
Ok(is_git_ignored) => is_git_ignored,
|
|
800
|
+
Err(e) => {
|
|
801
|
+
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
802
|
+
Some(absolute_file_or_directory),
|
|
803
|
+
Some(e.to_string()),
|
|
804
|
+
)));
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
if is_git_ignored && !bypass_git {
|
|
809
|
+
// println!("skipping directory: {}", path_str);
|
|
810
|
+
tracing::info!(
|
|
811
|
+
"skipping directory because its git ignored: {}",
|
|
812
|
+
path_str
|
|
813
|
+
);
|
|
814
|
+
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
815
|
+
Some(absolute_file_or_directory),
|
|
816
|
+
Some("Directory is git ignored!".to_string()),
|
|
817
|
+
)));
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
let entries = fs::read_dir(absolute_file_or_directory);
|
|
546
821
|
match entries {
|
|
547
822
|
Ok(_) => (),
|
|
548
823
|
Err(e) => {
|
|
549
824
|
return Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
550
|
-
Some(
|
|
825
|
+
Some(absolute_file_or_directory),
|
|
551
826
|
Some(e.to_string()),
|
|
552
827
|
)));
|
|
553
828
|
}
|
|
@@ -567,13 +842,19 @@ impl MerkleNode {
|
|
|
567
842
|
match entry {
|
|
568
843
|
Ok(entry) => {
|
|
569
844
|
children.push(
|
|
570
|
-
MerkleNode::construct_node(
|
|
571
|
-
.
|
|
845
|
+
MerkleNode::construct_node(
|
|
846
|
+
&entry.path(),
|
|
847
|
+
Some(node.clone()),
|
|
848
|
+
ignored_files,
|
|
849
|
+
absolute_root_path,
|
|
850
|
+
bypass_git,
|
|
851
|
+
)
|
|
852
|
+
.await,
|
|
572
853
|
);
|
|
573
854
|
}
|
|
574
855
|
Err(e) => {
|
|
575
856
|
children.push(Arc::new(RwLock::new(MerkleNode::empty_node(
|
|
576
|
-
Some(
|
|
857
|
+
Some(absolute_file_or_directory),
|
|
577
858
|
Some(e.to_string()),
|
|
578
859
|
))));
|
|
579
860
|
}
|
|
@@ -593,23 +874,33 @@ impl MerkleNode {
|
|
|
593
874
|
}
|
|
594
875
|
|
|
595
876
|
async fn construct_file_node(
|
|
596
|
-
|
|
877
|
+
absolute_file_path: &Path,
|
|
597
878
|
parent: ParentPtr,
|
|
879
|
+
ignored_files: &IgnoredFiles,
|
|
598
880
|
) -> Result<MerkleNode, String> {
|
|
599
|
-
let file_str =
|
|
881
|
+
let file_str = absolute_file_path
|
|
600
882
|
.to_str()
|
|
601
883
|
.ok_or("Could not convert file path to string!")?
|
|
602
884
|
.to_string();
|
|
603
885
|
// first see if it passes the
|
|
604
|
-
match file_utils::is_good_file(
|
|
886
|
+
match file_utils::is_good_file(absolute_file_path) {
|
|
605
887
|
Ok(_) => {}
|
|
606
888
|
Err(e) => {
|
|
607
889
|
return Err(format!("File failed runtime checks! {}", e.to_string()));
|
|
608
890
|
}
|
|
609
891
|
}
|
|
610
892
|
|
|
893
|
+
// check if the file is in the git ignore buffer.
|
|
894
|
+
// this is a bug right because we are not checking absoluteness here.
|
|
895
|
+
match ignored_files.contains(&file_str) {
|
|
896
|
+
true => {
|
|
897
|
+
return Err(format!("File is in git ignore buffer!"));
|
|
898
|
+
}
|
|
899
|
+
false => {}
|
|
900
|
+
}
|
|
901
|
+
|
|
611
902
|
// read the file_content to a buffer
|
|
612
|
-
let file_content = match tokio::fs::read(
|
|
903
|
+
let file_content = match tokio::fs::read(absolute_file_path).await {
|
|
613
904
|
Ok(content) => content,
|
|
614
905
|
Err(e) => {
|
|
615
906
|
return Err(format!("Could not read file! {}", e.to_string()));
|
|
@@ -617,7 +908,11 @@ impl MerkleNode {
|
|
|
617
908
|
};
|
|
618
909
|
|
|
619
910
|
// check if the file passes runtime checks.
|
|
620
|
-
match file_utils::is_good_file_runtime_check(
|
|
911
|
+
match file_utils::is_good_file_runtime_check(
|
|
912
|
+
absolute_file_path,
|
|
913
|
+
&file_content,
|
|
914
|
+
)
|
|
915
|
+
.await
|
|
621
916
|
{
|
|
622
917
|
Ok(_) => {}
|
|
623
918
|
Err(e) => {
|
|
@@ -648,15 +943,22 @@ impl MerkleNode {
|
|
|
648
943
|
}
|
|
649
944
|
|
|
650
945
|
async fn construct_file_node_or_error_node(
|
|
651
|
-
|
|
946
|
+
absolute_file_path: &Path,
|
|
652
947
|
parent: ParentPtr,
|
|
948
|
+
ignored_files: &IgnoredFiles,
|
|
653
949
|
) -> MerkleNode {
|
|
654
|
-
let node = match MerkleNode::construct_file_node(
|
|
950
|
+
let node = match MerkleNode::construct_file_node(
|
|
951
|
+
absolute_file_path,
|
|
952
|
+
parent,
|
|
953
|
+
ignored_files,
|
|
954
|
+
)
|
|
955
|
+
.await
|
|
956
|
+
{
|
|
655
957
|
Ok(node) => node,
|
|
656
958
|
Err(e) => {
|
|
657
959
|
// println!("constructing error node. error: {}", e);
|
|
658
|
-
|
|
659
|
-
MerkleNode::empty_node(Some(
|
|
960
|
+
// println!("file_path: {:?}", file_path);
|
|
961
|
+
MerkleNode::empty_node(Some(absolute_file_path), Some(e))
|
|
660
962
|
}
|
|
661
963
|
};
|
|
662
964
|
|