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