@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/lib.rs
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
#![deny(clippy::all)]
|
|
2
|
+
#![deny(unsafe_op_in_unsafe_fn)]
|
|
2
3
|
pub mod file_utils;
|
|
3
4
|
pub mod git_utils;
|
|
4
5
|
pub mod merkle_tree;
|
|
5
6
|
|
|
7
|
+
use std::vec;
|
|
8
|
+
|
|
6
9
|
use merkle_tree::{LocalConstruction, MerkleTree};
|
|
10
|
+
use tracing::{info, Level};
|
|
11
|
+
use tracing_appender::rolling::{RollingFileAppender, Rotation};
|
|
12
|
+
use tracing_subscriber::fmt;
|
|
7
13
|
|
|
8
14
|
#[macro_use]
|
|
9
15
|
extern crate napi_derive;
|
|
@@ -12,15 +18,35 @@ extern crate napi_derive;
|
|
|
12
18
|
pub struct MerkleClient {
|
|
13
19
|
tree: MerkleTree,
|
|
14
20
|
root_directory: String,
|
|
21
|
+
_guard: tracing_appender::non_blocking::WorkerGuard,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn init_logger() -> tracing_appender::non_blocking::WorkerGuard {
|
|
25
|
+
let file_appender =
|
|
26
|
+
RollingFileAppender::new(Rotation::NEVER, "./", "rust_log.txt");
|
|
27
|
+
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
|
28
|
+
let subscriber = fmt::Subscriber::builder()
|
|
29
|
+
.with_max_level(Level::TRACE)
|
|
30
|
+
.with_writer(non_blocking)
|
|
31
|
+
.with_ansi(false)
|
|
32
|
+
.with_line_number(true)
|
|
33
|
+
.finish();
|
|
34
|
+
|
|
35
|
+
let _ = tracing::subscriber::set_global_default(subscriber);
|
|
36
|
+
|
|
37
|
+
_guard
|
|
15
38
|
}
|
|
16
39
|
|
|
17
40
|
#[napi]
|
|
18
41
|
impl MerkleClient {
|
|
19
42
|
#[napi(constructor)]
|
|
20
43
|
pub fn new(root_directory: String) -> MerkleClient {
|
|
44
|
+
let _guard = init_logger();
|
|
45
|
+
|
|
21
46
|
MerkleClient {
|
|
22
47
|
tree: MerkleTree::empty_tree(),
|
|
23
48
|
root_directory,
|
|
49
|
+
_guard,
|
|
24
50
|
}
|
|
25
51
|
}
|
|
26
52
|
|
|
@@ -29,16 +55,19 @@ impl MerkleClient {
|
|
|
29
55
|
// 1. compute the merkle tree
|
|
30
56
|
// 2. update the backend
|
|
31
57
|
// 3. sync with the remote
|
|
32
|
-
|
|
58
|
+
info!("Merkle tree compute started!");
|
|
59
|
+
unsafe {
|
|
60
|
+
self.compute_merkle_tree().await?;
|
|
61
|
+
}
|
|
33
62
|
|
|
34
|
-
|
|
63
|
+
Ok(())
|
|
35
64
|
}
|
|
36
65
|
|
|
37
66
|
pub async unsafe fn interrupt(&mut self) -> Result<(), napi::Error> {
|
|
38
67
|
unimplemented!("Interrupt is not implemented yet");
|
|
39
68
|
}
|
|
40
69
|
|
|
41
|
-
|
|
70
|
+
#[napi]
|
|
42
71
|
pub async unsafe fn compute_merkle_tree(
|
|
43
72
|
&mut self,
|
|
44
73
|
) -> Result<(), napi::Error> {
|
|
@@ -67,59 +96,159 @@ impl MerkleClient {
|
|
|
67
96
|
let _r = self.tree.delete_file(file_path);
|
|
68
97
|
}
|
|
69
98
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
99
|
+
#[napi]
|
|
100
|
+
pub async fn get_subtree_hash(
|
|
101
|
+
&self,
|
|
102
|
+
relative_path: String,
|
|
103
|
+
) -> Result<String, napi::Error> {
|
|
104
|
+
let absolute_path =
|
|
105
|
+
std::path::Path::new(&self.root_directory).join(&relative_path);
|
|
106
|
+
|
|
107
|
+
let canonical_path = match absolute_path.canonicalize() {
|
|
108
|
+
Ok(path) => path,
|
|
109
|
+
Err(e) => return Err(napi::Error::new(
|
|
110
|
+
napi::Status::Unknown,
|
|
111
|
+
format!("Error in canonicalizing path: {:?}", e),
|
|
112
|
+
)),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
let hash = self.tree.get_subtree_hash(canonical_path.clone()).await;
|
|
116
|
+
|
|
117
|
+
match hash {
|
|
118
|
+
Ok(hash) => Ok(hash),
|
|
119
|
+
Err(e) => Err(napi::Error::new(
|
|
120
|
+
napi::Status::Unknown,
|
|
121
|
+
format!("Error in get_subtree_hash. \nRelative path: {:?}, \nAbsolute path: {:?}, \nCanonical path: {:?}, \nRoot directory: {:?}\nError: {:?}", &relative_path, absolute_path, canonical_path, self.root_directory, e),
|
|
122
|
+
)),
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#[napi]
|
|
127
|
+
pub async fn get_num_embeddable_files(&self) -> Result<i32, napi::Error> {
|
|
128
|
+
let num = self.tree.get_num_embeddable_files().await;
|
|
129
|
+
|
|
130
|
+
match num {
|
|
131
|
+
Ok(num) => Ok(num),
|
|
132
|
+
Err(e) => Err(napi::Error::new(
|
|
133
|
+
napi::Status::Unknown,
|
|
134
|
+
format!("Error in get_num_embeddable_files: {:?}", e),
|
|
135
|
+
)),
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
pub async fn get_num_embeddable_files_in_subtree(
|
|
140
|
+
&self,
|
|
141
|
+
relative_path: String,
|
|
142
|
+
) -> Result<i32, napi::Error> {
|
|
143
|
+
let absolute_path = std::path::Path::new(&self.root_directory)
|
|
144
|
+
.join(relative_path)
|
|
145
|
+
.canonicalize()?;
|
|
146
|
+
|
|
147
|
+
let num = self
|
|
148
|
+
.tree
|
|
149
|
+
.get_num_embeddable_files_in_subtree(absolute_path)
|
|
150
|
+
.await;
|
|
151
|
+
|
|
152
|
+
match num {
|
|
153
|
+
Ok(num) => Ok(num),
|
|
154
|
+
Err(e) => Err(napi::Error::new(
|
|
155
|
+
napi::Status::Unknown,
|
|
156
|
+
format!("Error in get_num_embeddable_files_in_subtree: {:?}", e),
|
|
157
|
+
)),
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#[napi]
|
|
162
|
+
pub async fn get_all_files(&self) -> Result<Vec<String>, napi::Error> {
|
|
163
|
+
let files = self.tree.get_all_files().await;
|
|
164
|
+
|
|
165
|
+
match files {
|
|
166
|
+
Ok(files) => Ok(files),
|
|
167
|
+
Err(e) => Err(napi::Error::new(
|
|
168
|
+
napi::Status::Unknown,
|
|
169
|
+
format!("Error in get_all_files: {:?}", e),
|
|
170
|
+
)),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#[napi]
|
|
175
|
+
pub async fn get_all_dir_files_to_embed(
|
|
176
|
+
&self,
|
|
177
|
+
absolute_file_path: String,
|
|
178
|
+
) -> Result<Vec<String>, napi::Error> {
|
|
179
|
+
let absolute_path_str = absolute_file_path.as_str();
|
|
180
|
+
let files = self
|
|
181
|
+
.tree
|
|
182
|
+
.get_all_dir_files_to_embed(absolute_path_str)
|
|
183
|
+
.await;
|
|
184
|
+
|
|
185
|
+
match files {
|
|
186
|
+
Ok(files) => Ok(files),
|
|
187
|
+
Err(e) => Err(napi::Error::new(
|
|
188
|
+
napi::Status::Unknown,
|
|
189
|
+
format!("Error in get_all_dir_files_to_embed: {:?}", e),
|
|
190
|
+
)),
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
#[napi]
|
|
195
|
+
pub async unsafe fn get_next_file_to_embed(
|
|
196
|
+
&mut self,
|
|
197
|
+
) -> Result<Vec<String>, napi::Error> {
|
|
198
|
+
let n = self.tree.get_next_file_to_embed().await;
|
|
199
|
+
|
|
200
|
+
match n {
|
|
201
|
+
Ok((file, path)) => {
|
|
202
|
+
// now our job is to put the filename as the first element of the path.
|
|
203
|
+
|
|
204
|
+
// TODO(sualeh): we should assert that the path is ascending up to the path.
|
|
205
|
+
|
|
206
|
+
let ret = vec![file];
|
|
207
|
+
let ret = ret.into_iter().chain(path.into_iter()).collect::<Vec<_>>();
|
|
208
|
+
Ok(ret)
|
|
209
|
+
}
|
|
210
|
+
Err(e) => Err(napi::Error::new(
|
|
211
|
+
napi::Status::Unknown,
|
|
212
|
+
format!("Error in get_next_file_to_embed: {:?}", e),
|
|
213
|
+
)),
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// FIXME(sualeh): get_spline
|
|
218
|
+
#[napi]
|
|
219
|
+
pub async fn get_spline(
|
|
220
|
+
&self,
|
|
221
|
+
absolute_file_path: String,
|
|
222
|
+
) -> Result<Vec<String>, napi::Error> {
|
|
223
|
+
let absolute_path_str = absolute_file_path.as_str();
|
|
224
|
+
let spline = self.tree.get_spline(absolute_path_str).await;
|
|
225
|
+
|
|
226
|
+
match spline {
|
|
227
|
+
Ok(spline) => Ok(spline),
|
|
228
|
+
Err(e) => Err(napi::Error::new(
|
|
229
|
+
napi::Status::Unknown,
|
|
230
|
+
format!("Error in get_spline: {:?}", e),
|
|
231
|
+
)),
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
#[napi]
|
|
236
|
+
pub async fn get_hashes_for_files(
|
|
237
|
+
&self,
|
|
238
|
+
files: Vec<String>,
|
|
239
|
+
) -> Result<Vec<String>, napi::Error> {
|
|
240
|
+
let hashes = self.tree.get_hashes_for_files(files).await;
|
|
241
|
+
|
|
242
|
+
match hashes {
|
|
243
|
+
Ok(hashes) => Ok(hashes),
|
|
244
|
+
Err(e) => Err(napi::Error::new(
|
|
245
|
+
napi::Status::Unknown,
|
|
246
|
+
format!("Error in get_hashes_for_files: {:?}", e),
|
|
247
|
+
)),
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
#[napi]
|
|
123
252
|
pub fn update_root_directory(&mut self, root_directory: String) {
|
|
124
253
|
self.root_directory = root_directory;
|
|
125
254
|
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
use crate::git_utils;
|
|
1
2
|
use crate::merkle_tree::{
|
|
2
3
|
File, MerkleNode, MerkleNodePtr, NodeType, PinnedFuture,
|
|
3
4
|
};
|
|
4
5
|
|
|
5
6
|
use super::{LocalConstruction, MerkleTree};
|
|
6
|
-
use std::
|
|
7
|
-
use std::
|
|
7
|
+
use std::collections::{BTreeMap, HashSet};
|
|
8
|
+
use std::path::{Path, PathBuf};
|
|
8
9
|
use tonic::async_trait;
|
|
9
10
|
|
|
10
11
|
#[async_trait]
|
|
11
12
|
impl LocalConstruction for MerkleTree {
|
|
12
|
-
async fn new(
|
|
13
|
+
async fn new(
|
|
14
|
+
root_directory: Option<String>,
|
|
15
|
+
) -> Result<MerkleTree, anyhow::Error> {
|
|
13
16
|
if let Some(root_directory) = root_directory {
|
|
14
17
|
let n = MerkleTree::construct_merkle_tree(root_directory).await;
|
|
15
18
|
return n;
|
|
@@ -25,38 +28,59 @@ impl LocalConstruction for MerkleTree {
|
|
|
25
28
|
/// 2. compute hash for each file
|
|
26
29
|
/// 3. construct merkle tree
|
|
27
30
|
/// 4. return merkle tree
|
|
28
|
-
async fn construct_merkle_tree(
|
|
29
|
-
|
|
31
|
+
async fn construct_merkle_tree(
|
|
32
|
+
absolute_path_to_root_directory: String,
|
|
33
|
+
) -> Result<MerkleTree, anyhow::Error> {
|
|
34
|
+
let path = PathBuf::from(absolute_path_to_root_directory.clone());
|
|
30
35
|
if !path.exists() {
|
|
31
36
|
// FIXME: we should report this via a good logger.
|
|
32
37
|
panic!("Root directory does not exist!");
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
// 1. get all the gitignored files
|
|
41
|
+
let git_ignored_files = match git_utils::list_ignored_files(
|
|
42
|
+
absolute_path_to_root_directory.as_str(),
|
|
43
|
+
true,
|
|
44
|
+
) {
|
|
45
|
+
Ok(git_ignored) => git_ignored,
|
|
46
|
+
Err(_e) => HashSet::new(),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let root_node = MerkleNode::new(
|
|
50
|
+
path,
|
|
51
|
+
None,
|
|
52
|
+
&git_ignored_files,
|
|
53
|
+
absolute_path_to_root_directory.as_str(),
|
|
54
|
+
)
|
|
55
|
+
.await;
|
|
36
56
|
let mut mt = MerkleTree {
|
|
37
57
|
root: root_node,
|
|
38
|
-
files:
|
|
39
|
-
root_path:
|
|
58
|
+
files: BTreeMap::new(),
|
|
59
|
+
root_path: absolute_path_to_root_directory,
|
|
60
|
+
cursor: None,
|
|
61
|
+
git_ignored_files,
|
|
40
62
|
};
|
|
41
63
|
|
|
42
64
|
// we now iterate over all the nodes and add them to the hashmap
|
|
43
65
|
// TODO(later): i can make this parallel.
|
|
44
66
|
fn add_nodes_to_hashmap<'a>(
|
|
45
67
|
node: &'a MerkleNodePtr,
|
|
46
|
-
files: &'a mut
|
|
68
|
+
files: &'a mut BTreeMap<String, File>,
|
|
47
69
|
) -> PinnedFuture<'a, ()> {
|
|
48
70
|
Box::pin(async move {
|
|
49
71
|
let node_reader = node.read().await;
|
|
50
72
|
match &node_reader.node_type {
|
|
51
73
|
NodeType::Branch(n) => {
|
|
52
|
-
|
|
53
|
-
|
|
74
|
+
tracing::info!("Branch: {:?}", n.0);
|
|
75
|
+
let children = &n.1;
|
|
76
|
+
files.insert(n.0.clone(), File { node: node.clone() });
|
|
54
77
|
for child in children {
|
|
55
78
|
add_nodes_to_hashmap(child, files).await;
|
|
56
79
|
}
|
|
57
80
|
}
|
|
58
81
|
NodeType::File(file_name) => {
|
|
59
82
|
let f = File { node: node.clone() };
|
|
83
|
+
tracing::info!("File: {:?}", file_name);
|
|
60
84
|
files.insert(file_name.clone(), f);
|
|
61
85
|
}
|
|
62
86
|
NodeType::ErrorNode(_) => {
|
|
@@ -68,6 +92,9 @@ impl LocalConstruction for MerkleTree {
|
|
|
68
92
|
|
|
69
93
|
add_nodes_to_hashmap(&mt.root, &mut mt.files).await;
|
|
70
94
|
|
|
95
|
+
tracing::info!("Merkle tree compute finished!");
|
|
96
|
+
tracing::info!("Merkle tree: {}", mt);
|
|
97
|
+
|
|
71
98
|
Ok(mt)
|
|
72
99
|
}
|
|
73
100
|
|
|
@@ -102,9 +129,9 @@ impl LocalConstruction for MerkleTree {
|
|
|
102
129
|
// File does not exist in the tree, let's add it.
|
|
103
130
|
let e = self.attach_new_node_to_tree(file_path.clone()).await;
|
|
104
131
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
132
|
+
if e.is_err() {
|
|
133
|
+
return Err(anyhow::anyhow!("Could not attach new node to tree!"));
|
|
134
|
+
}
|
|
108
135
|
}
|
|
109
136
|
|
|
110
137
|
Ok(())
|
|
@@ -139,18 +166,17 @@ impl LocalConstruction for MerkleTree {
|
|
|
139
166
|
let parent_node = parent_node.unwrap();
|
|
140
167
|
let mut mut_parent = parent_node.write().await;
|
|
141
168
|
|
|
169
|
+
// BUG(sualeh): need to actually drop everything that is a child here.
|
|
170
|
+
// idea: enumerate all nodes that are children of this through a starts with query on the hashtable.
|
|
171
|
+
// then drop all of them.
|
|
172
|
+
// in opposite order of length.
|
|
142
173
|
|
|
143
|
-
|
|
144
|
-
// idea: enumerate all nodes that are children of this through a starts with query on the hashtable.
|
|
145
|
-
// then drop all of them.
|
|
146
|
-
// in opposite order of length.
|
|
147
|
-
|
|
148
|
-
// then remove the node from the parent and you are done
|
|
174
|
+
// then remove the node from the parent and you are done
|
|
149
175
|
|
|
150
176
|
// Remove the child from the parent node
|
|
151
177
|
match mut_parent.node_type {
|
|
152
178
|
NodeType::Branch(ref mut node) => {
|
|
153
|
-
|
|
179
|
+
let children = &mut node.1;
|
|
154
180
|
let mut found = false;
|
|
155
181
|
let mut index = 0;
|
|
156
182
|
|