@anysphere/file-service 0.0.0-e3fdf62d → 0.0.0-e7e53a0a
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 +15 -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 +201 -16
- package/src/merkle_tree/local_construction.rs +48 -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/lib.rs
CHANGED
|
@@ -1,37 +1,118 @@
|
|
|
1
|
+
#![windows_subsystem = "windows"]
|
|
1
2
|
#![deny(clippy::all)]
|
|
3
|
+
#![deny(unsafe_op_in_unsafe_fn)]
|
|
2
4
|
pub mod file_utils;
|
|
3
|
-
pub mod git_utils;
|
|
4
5
|
pub mod merkle_tree;
|
|
5
6
|
|
|
6
|
-
use std::vec;
|
|
7
|
+
use std::{collections::HashSet, vec};
|
|
7
8
|
|
|
9
|
+
use anyhow::Context;
|
|
8
10
|
use merkle_tree::{LocalConstruction, MerkleTree};
|
|
11
|
+
use tracing::{info, subscriber, Level};
|
|
12
|
+
use tracing_appender::non_blocking::WorkerGuard;
|
|
13
|
+
use tracing_appender::rolling::{RollingFileAppender, Rotation};
|
|
14
|
+
use tracing_subscriber::fmt;
|
|
15
|
+
use tracing_subscriber::prelude::*;
|
|
9
16
|
|
|
10
17
|
#[macro_use]
|
|
11
18
|
extern crate napi_derive;
|
|
12
19
|
|
|
20
|
+
pub enum GuardType {
|
|
21
|
+
#[cfg(all(not(feature = "debugfile"), not(target_os = "linux")))]
|
|
22
|
+
Guard(tracing_axiom::Guard),
|
|
23
|
+
WorkerGuard(tracing_appender::non_blocking::WorkerGuard),
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
#[napi]
|
|
14
27
|
pub struct MerkleClient {
|
|
15
28
|
tree: MerkleTree,
|
|
16
|
-
|
|
29
|
+
absolute_root_directory: String,
|
|
30
|
+
_guard: Option<GuardType>,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn init_logger() -> Option<GuardType> {
|
|
34
|
+
#[cfg(feature = "debugfile")]
|
|
35
|
+
let _guard = {
|
|
36
|
+
let file_appender =
|
|
37
|
+
RollingFileAppender::new(Rotation::NEVER, "./", "rust_log.txt");
|
|
38
|
+
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
|
39
|
+
let subscriber = fmt::Subscriber::builder()
|
|
40
|
+
.with_max_level(Level::TRACE)
|
|
41
|
+
.with_writer(non_blocking)
|
|
42
|
+
.with_ansi(false)
|
|
43
|
+
.with_line_number(true)
|
|
44
|
+
.finish();
|
|
45
|
+
|
|
46
|
+
let _ = tracing::subscriber::set_global_default(subscriber);
|
|
47
|
+
|
|
48
|
+
Some(GuardType::WorkerGuard(_guard))
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
#[cfg(all(not(feature = "debugfile"), not(target_os = "linux")))]
|
|
52
|
+
let _guard = {
|
|
53
|
+
let (axiom_layer, _guard) = tracing_axiom::builder()
|
|
54
|
+
.with_token("xaat-a51088e6-7889-41c0-b440-cfd4601acdd7")
|
|
55
|
+
.with_dataset("local-indexing")
|
|
56
|
+
.layer()
|
|
57
|
+
.ok()?;
|
|
58
|
+
// let fmt_layer = fmt::layer().with_level(true).with_ansi(false).with_line_number(true);
|
|
59
|
+
|
|
60
|
+
let _ = tracing_subscriber::registry()
|
|
61
|
+
.with(axiom_layer)
|
|
62
|
+
.try_init()
|
|
63
|
+
.ok()?;
|
|
64
|
+
// let _ = tracing::subscriber::set_global_default(subscriber);
|
|
65
|
+
|
|
66
|
+
info!("Tracing initialized! in rust");
|
|
67
|
+
|
|
68
|
+
Some(GuardType::Guard(_guard))
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
#[cfg(all(not(feature = "debugfile"), target_os = "linux"))]
|
|
72
|
+
let _guard = { None };
|
|
73
|
+
|
|
74
|
+
_guard
|
|
17
75
|
}
|
|
18
76
|
|
|
19
77
|
#[napi]
|
|
20
78
|
impl MerkleClient {
|
|
21
79
|
#[napi(constructor)]
|
|
22
|
-
pub fn new(
|
|
80
|
+
pub fn new(absolute_root_directory: String) -> MerkleClient {
|
|
81
|
+
let _guard = init_logger();
|
|
82
|
+
|
|
83
|
+
// let canonical_root_directory = std::path::Path::new(&absolute_root_directory);
|
|
84
|
+
// use dunce::canonicalize;
|
|
85
|
+
// let canonical_root_directory = match dunce::canonicalize(&canonical_root_directory) {
|
|
86
|
+
// Ok(path) => path.to_str().unwrap_or(&absolute_root_directory).to_string().to_lowercase(),
|
|
87
|
+
// Err(e) => {
|
|
88
|
+
// info!("Error in canonicalizing path: path: {:?}, error {:?}", canonical_root_directory, e);
|
|
89
|
+
// absolute_root_directory
|
|
90
|
+
// }
|
|
91
|
+
// };
|
|
92
|
+
|
|
23
93
|
MerkleClient {
|
|
24
94
|
tree: MerkleTree::empty_tree(),
|
|
25
|
-
|
|
95
|
+
absolute_root_directory,
|
|
96
|
+
_guard,
|
|
26
97
|
}
|
|
27
98
|
}
|
|
28
99
|
|
|
29
100
|
#[napi]
|
|
30
|
-
pub async unsafe fn init(
|
|
101
|
+
pub async unsafe fn init(
|
|
102
|
+
&mut self,
|
|
103
|
+
git_ignored_files: Vec<String>,
|
|
104
|
+
is_git_repo: bool,
|
|
105
|
+
) -> Result<(), napi::Error> {
|
|
31
106
|
// 1. compute the merkle tree
|
|
32
107
|
// 2. update the backend
|
|
33
108
|
// 3. sync with the remote
|
|
34
|
-
|
|
109
|
+
info!("Merkle tree compute started!");
|
|
110
|
+
info!("Root directory: {:?}", self.absolute_root_directory);
|
|
111
|
+
unsafe {
|
|
112
|
+
self
|
|
113
|
+
.compute_merkle_tree(git_ignored_files, is_git_repo)
|
|
114
|
+
.await?;
|
|
115
|
+
}
|
|
35
116
|
|
|
36
117
|
Ok(())
|
|
37
118
|
}
|
|
@@ -40,12 +121,27 @@ impl MerkleClient {
|
|
|
40
121
|
unimplemented!("Interrupt is not implemented yet");
|
|
41
122
|
}
|
|
42
123
|
|
|
43
|
-
|
|
124
|
+
#[napi]
|
|
44
125
|
pub async unsafe fn compute_merkle_tree(
|
|
45
126
|
&mut self,
|
|
127
|
+
git_ignored_files: Vec<String>,
|
|
128
|
+
is_git_repo: bool,
|
|
46
129
|
) -> Result<(), napi::Error> {
|
|
47
|
-
|
|
48
|
-
|
|
130
|
+
// make the git ignored files into a hash set
|
|
131
|
+
let mut git_ignored_set = HashSet::from_iter(git_ignored_files.into_iter());
|
|
132
|
+
|
|
133
|
+
// if the hashset itself contains the root directory, then we should remove it.
|
|
134
|
+
// this is because the root directory is not a file, and we don't want to ignore it.
|
|
135
|
+
if git_ignored_set.contains(&self.absolute_root_directory) {
|
|
136
|
+
git_ignored_set.remove(&self.absolute_root_directory);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let t = MerkleTree::construct_merkle_tree(
|
|
140
|
+
self.absolute_root_directory.clone(),
|
|
141
|
+
git_ignored_set,
|
|
142
|
+
is_git_repo,
|
|
143
|
+
)
|
|
144
|
+
.await;
|
|
49
145
|
|
|
50
146
|
match t {
|
|
51
147
|
Ok(tree) => {
|
|
@@ -72,15 +168,42 @@ impl MerkleClient {
|
|
|
72
168
|
#[napi]
|
|
73
169
|
pub async fn get_subtree_hash(
|
|
74
170
|
&self,
|
|
75
|
-
|
|
171
|
+
relative_path: String,
|
|
76
172
|
) -> Result<String, napi::Error> {
|
|
77
|
-
let
|
|
173
|
+
let relative_path_without_leading_slash = match relative_path
|
|
174
|
+
.strip_prefix('.')
|
|
175
|
+
{
|
|
176
|
+
Some(path) => path.strip_prefix(std::path::MAIN_SEPARATOR).unwrap_or(""),
|
|
177
|
+
None => relative_path.as_str(),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
let absolute_path = if !relative_path_without_leading_slash.is_empty() {
|
|
181
|
+
std::path::Path::new(&self.absolute_root_directory)
|
|
182
|
+
.join(relative_path_without_leading_slash)
|
|
183
|
+
} else {
|
|
184
|
+
std::path::Path::new(&self.absolute_root_directory).to_path_buf()
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
let absolute_path_string = match absolute_path.to_str() {
|
|
188
|
+
Some(path) => path.to_string(),
|
|
189
|
+
None => {
|
|
190
|
+
return Err(napi::Error::new(
|
|
191
|
+
napi::Status::Unknown,
|
|
192
|
+
format!("some string error"),
|
|
193
|
+
))
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
let hash = self
|
|
198
|
+
.tree
|
|
199
|
+
.get_subtree_hash(absolute_path_string.as_str())
|
|
200
|
+
.await;
|
|
78
201
|
|
|
79
202
|
match hash {
|
|
80
203
|
Ok(hash) => Ok(hash),
|
|
81
204
|
Err(e) => Err(napi::Error::new(
|
|
82
205
|
napi::Status::Unknown,
|
|
83
|
-
format!("Error in get_subtree_hash: {:?}", e)
|
|
206
|
+
format!("Error in get_subtree_hash. \nRelative path: {:?}, \nAbsolute path: {:?}, \nRoot directory: {:?}\nError: {:?}", &relative_path, absolute_path, self.absolute_root_directory, e)
|
|
84
207
|
)),
|
|
85
208
|
}
|
|
86
209
|
}
|
|
@@ -98,6 +221,28 @@ impl MerkleClient {
|
|
|
98
221
|
}
|
|
99
222
|
}
|
|
100
223
|
|
|
224
|
+
pub async fn get_num_embeddable_files_in_subtree(
|
|
225
|
+
&self,
|
|
226
|
+
relative_path: String,
|
|
227
|
+
) -> Result<i32, napi::Error> {
|
|
228
|
+
let absolute_path = std::path::Path::new(&self.absolute_root_directory)
|
|
229
|
+
.join(relative_path)
|
|
230
|
+
.canonicalize()?;
|
|
231
|
+
|
|
232
|
+
let num = self
|
|
233
|
+
.tree
|
|
234
|
+
.get_num_embeddable_files_in_subtree(absolute_path)
|
|
235
|
+
.await;
|
|
236
|
+
|
|
237
|
+
match num {
|
|
238
|
+
Ok(num) => Ok(num),
|
|
239
|
+
Err(e) => Err(napi::Error::new(
|
|
240
|
+
napi::Status::Unknown,
|
|
241
|
+
format!("Error in get_num_embeddable_files_in_subtree: {:?}", e),
|
|
242
|
+
)),
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
101
246
|
#[napi]
|
|
102
247
|
pub async fn get_all_files(&self) -> Result<Vec<String>, napi::Error> {
|
|
103
248
|
let files = self.tree.get_all_files().await;
|
|
@@ -111,6 +256,28 @@ impl MerkleClient {
|
|
|
111
256
|
}
|
|
112
257
|
}
|
|
113
258
|
|
|
259
|
+
#[napi]
|
|
260
|
+
pub async fn get_all_dir_files_to_embed(
|
|
261
|
+
&self,
|
|
262
|
+
absolute_file_path: String,
|
|
263
|
+
) -> Result<Vec<String>, napi::Error> {
|
|
264
|
+
// let absolute_path = absolute_file_path.to_lowercase();
|
|
265
|
+
// let absolute_path_str = absolute_path.as_str();
|
|
266
|
+
|
|
267
|
+
let files = self
|
|
268
|
+
.tree
|
|
269
|
+
.get_all_dir_files_to_embed(absolute_file_path.as_str())
|
|
270
|
+
.await;
|
|
271
|
+
|
|
272
|
+
match files {
|
|
273
|
+
Ok(files) => Ok(files),
|
|
274
|
+
Err(e) => Err(napi::Error::new(
|
|
275
|
+
napi::Status::Unknown,
|
|
276
|
+
format!("Error in get_all_dir_files_to_embed: {:?}", e),
|
|
277
|
+
)),
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
114
281
|
#[napi]
|
|
115
282
|
pub async unsafe fn get_next_file_to_embed(
|
|
116
283
|
&mut self,
|
|
@@ -125,7 +292,6 @@ impl MerkleClient {
|
|
|
125
292
|
|
|
126
293
|
let ret = vec![file];
|
|
127
294
|
let ret = ret.into_iter().chain(path.into_iter()).collect::<Vec<_>>();
|
|
128
|
-
|
|
129
295
|
Ok(ret)
|
|
130
296
|
}
|
|
131
297
|
Err(e) => Err(napi::Error::new(
|
|
@@ -135,6 +301,25 @@ impl MerkleClient {
|
|
|
135
301
|
}
|
|
136
302
|
}
|
|
137
303
|
|
|
304
|
+
// FIXME(sualeh): get_spline
|
|
305
|
+
#[napi]
|
|
306
|
+
pub async fn get_spline(
|
|
307
|
+
&self,
|
|
308
|
+
absolute_file_path: String,
|
|
309
|
+
) -> Result<Vec<String>, napi::Error> {
|
|
310
|
+
// let absolute_path = absolute_file_path.to_lowercase();
|
|
311
|
+
// let absolute_path_str = absolute_path.as_str();
|
|
312
|
+
let spline = self.tree.get_spline(absolute_file_path.as_str()).await;
|
|
313
|
+
|
|
314
|
+
match spline {
|
|
315
|
+
Ok(spline) => Ok(spline),
|
|
316
|
+
Err(e) => Err(napi::Error::new(
|
|
317
|
+
napi::Status::Unknown,
|
|
318
|
+
format!("Error in get_spline: {:?}", e),
|
|
319
|
+
)),
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
138
323
|
#[napi]
|
|
139
324
|
pub async fn get_hashes_for_files(
|
|
140
325
|
&self,
|
|
@@ -151,8 +336,8 @@ impl MerkleClient {
|
|
|
151
336
|
}
|
|
152
337
|
}
|
|
153
338
|
|
|
154
|
-
|
|
339
|
+
#[napi]
|
|
155
340
|
pub fn update_root_directory(&mut self, root_directory: String) {
|
|
156
|
-
self.
|
|
341
|
+
self.absolute_root_directory = root_directory;
|
|
157
342
|
}
|
|
158
343
|
}
|
|
@@ -3,17 +3,23 @@ use crate::merkle_tree::{
|
|
|
3
3
|
};
|
|
4
4
|
|
|
5
5
|
use super::{LocalConstruction, MerkleTree};
|
|
6
|
-
use std::
|
|
7
|
-
use std::
|
|
6
|
+
use std::collections::{BTreeMap, HashSet};
|
|
7
|
+
use std::path::{Path, PathBuf};
|
|
8
8
|
use tonic::async_trait;
|
|
9
9
|
|
|
10
10
|
#[async_trait]
|
|
11
11
|
impl LocalConstruction for MerkleTree {
|
|
12
|
+
#[tracing::instrument]
|
|
12
13
|
async fn new(
|
|
13
14
|
root_directory: Option<String>,
|
|
14
15
|
) -> Result<MerkleTree, anyhow::Error> {
|
|
16
|
+
let git_ignored_files = HashSet::<String>::new();
|
|
15
17
|
if let Some(root_directory) = root_directory {
|
|
16
|
-
let n = MerkleTree::construct_merkle_tree(
|
|
18
|
+
let n = MerkleTree::construct_merkle_tree(
|
|
19
|
+
root_directory,
|
|
20
|
+
git_ignored_files,
|
|
21
|
+
false
|
|
22
|
+
).await;
|
|
17
23
|
return n;
|
|
18
24
|
}
|
|
19
25
|
|
|
@@ -27,28 +33,50 @@ impl LocalConstruction for MerkleTree {
|
|
|
27
33
|
/// 2. compute hash for each file
|
|
28
34
|
/// 3. construct merkle tree
|
|
29
35
|
/// 4. return merkle tree
|
|
36
|
+
#[tracing::instrument(skip(git_ignored_files_and_dirs))]
|
|
30
37
|
async fn construct_merkle_tree(
|
|
31
|
-
|
|
38
|
+
absolute_path_to_root_directory: String,
|
|
39
|
+
git_ignored_files_and_dirs: HashSet<String>,
|
|
40
|
+
is_git_repo: bool
|
|
32
41
|
) -> Result<MerkleTree, anyhow::Error> {
|
|
33
|
-
let path = PathBuf::from(
|
|
42
|
+
let path = PathBuf::from(absolute_path_to_root_directory.clone());
|
|
34
43
|
if !path.exists() {
|
|
35
44
|
// FIXME: we should report this via a good logger.
|
|
36
45
|
panic!("Root directory does not exist!");
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
|
|
48
|
+
// 1. get all the gitignored files
|
|
49
|
+
// let git_ignored_files_and_dirs =
|
|
50
|
+
// match git_utils::list_ignored_files_and_directories(
|
|
51
|
+
// absolute_path_to_root_directory.as_str(),
|
|
52
|
+
// true,
|
|
53
|
+
// ) {
|
|
54
|
+
// Ok(git_ignored) => git_ignored,
|
|
55
|
+
// Err(_e) => HashSet::new(),
|
|
56
|
+
// };
|
|
57
|
+
|
|
58
|
+
let root_node = MerkleNode::new(
|
|
59
|
+
path,
|
|
60
|
+
None,
|
|
61
|
+
&git_ignored_files_and_dirs,
|
|
62
|
+
absolute_path_to_root_directory.as_str(),
|
|
63
|
+
is_git_repo
|
|
64
|
+
).await;
|
|
65
|
+
|
|
40
66
|
let mut mt = MerkleTree {
|
|
41
67
|
root: root_node,
|
|
42
|
-
files:
|
|
43
|
-
root_path:
|
|
68
|
+
files: BTreeMap::new(),
|
|
69
|
+
root_path: absolute_path_to_root_directory,
|
|
44
70
|
cursor: None,
|
|
71
|
+
git_ignored_files_and_dirs,
|
|
72
|
+
is_git_repo
|
|
45
73
|
};
|
|
46
74
|
|
|
47
75
|
// we now iterate over all the nodes and add them to the hashmap
|
|
48
76
|
// TODO(later): i can make this parallel.
|
|
49
77
|
fn add_nodes_to_hashmap<'a>(
|
|
50
78
|
node: &'a MerkleNodePtr,
|
|
51
|
-
files: &'a mut
|
|
79
|
+
files: &'a mut BTreeMap<String, File>,
|
|
52
80
|
) -> PinnedFuture<'a, ()> {
|
|
53
81
|
Box::pin(async move {
|
|
54
82
|
let node_reader = node.read().await;
|
|
@@ -62,6 +90,13 @@ impl LocalConstruction for MerkleTree {
|
|
|
62
90
|
}
|
|
63
91
|
NodeType::File(file_name) => {
|
|
64
92
|
let f = File { node: node.clone() };
|
|
93
|
+
|
|
94
|
+
// i dont reallly like this :(((
|
|
95
|
+
// let canonical_file_name = match dunce::canonicalize(file_name) {
|
|
96
|
+
// Ok(path) => path.to_str().unwrap_or(file_name).to_string(),
|
|
97
|
+
// Err(_) => file_name.clone(),
|
|
98
|
+
// };
|
|
99
|
+
|
|
65
100
|
files.insert(file_name.clone(), f);
|
|
66
101
|
}
|
|
67
102
|
NodeType::ErrorNode(_) => {
|
|
@@ -73,9 +108,12 @@ impl LocalConstruction for MerkleTree {
|
|
|
73
108
|
|
|
74
109
|
add_nodes_to_hashmap(&mt.root, &mut mt.files).await;
|
|
75
110
|
|
|
111
|
+
tracing::info!("number of files in the tree: {}", mt.files.len());
|
|
112
|
+
|
|
76
113
|
Ok(mt)
|
|
77
114
|
}
|
|
78
115
|
|
|
116
|
+
#[tracing::instrument]
|
|
79
117
|
async fn update_file(
|
|
80
118
|
&mut self,
|
|
81
119
|
file_path: String,
|
|
@@ -115,6 +153,7 @@ impl LocalConstruction for MerkleTree {
|
|
|
115
153
|
Ok(())
|
|
116
154
|
}
|
|
117
155
|
|
|
156
|
+
#[tracing::instrument]
|
|
118
157
|
async fn delete_file(
|
|
119
158
|
&mut self,
|
|
120
159
|
file_path: String,
|