@anysphere/file-service 0.0.0-ebd74baa → 0.0.0-ec067eac

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/src/git_utils.rs DELETED
@@ -1,355 +0,0 @@
1
- use std::collections::HashSet;
2
- use std::path::MAIN_SEPARATOR_STR;
3
- use std::process::Command;
4
-
5
- pub fn list_ignored_files_and_directories(
6
- workspace_root_path: &str,
7
- should_return_absolute_paths: bool,
8
- ) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
9
- let mut gitignored_files = HashSet::new();
10
-
11
- let commands = vec![
12
- vec![
13
- "git",
14
- "ls-files",
15
- "--others",
16
- "--ignored",
17
- "--exclude-standard",
18
- "--directory",
19
- "--no-empty-directory"
20
- ],
21
- // FIXME(sualeh): this is super sketchy and might totally break in like a bazillion ways. i dont like it.
22
- vec![
23
- "git submodule foreach --quiet 'git -C $toplevel/$path ls-files --others --ignored --exclude-standard --directory --no-empty-directory | (while read line; do echo $path/$line; done)'",
24
- ],
25
- ];
26
-
27
- for command in commands {
28
- let output = Command::new(&command[0])
29
- .args(&command[1..])
30
- .current_dir(workspace_root_path)
31
- .output()?;
32
-
33
- if output.status.success() {
34
- let files = String::from_utf8(output.stdout)?
35
- .lines()
36
- .filter(|line| !line.is_empty())
37
- .map(|line| {
38
- let line = line.replace("/", MAIN_SEPARATOR_STR);
39
-
40
- if should_return_absolute_paths {
41
- let mut path = std::path::PathBuf::from(workspace_root_path);
42
-
43
- path.push(line.clone());
44
-
45
- match path.canonicalize() {
46
- Ok(canonical_path) => {
47
- canonical_path.to_string_lossy().into_owned()
48
- }
49
- Err(_) => String::from(line),
50
- }
51
- } else {
52
- String::from(line)
53
- }
54
- })
55
- .collect::<HashSet<_>>();
56
-
57
- gitignored_files.extend(files);
58
- }
59
- }
60
-
61
- Ok(gitignored_files)
62
- }
63
-
64
- pub fn list_ignored_files_with_absolute_paths(
65
- workspace_root_path: &str,
66
- ) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
67
- let mut gitignored_files = HashSet::new();
68
-
69
- let commands = vec![
70
- vec![
71
- "git",
72
- "ls-files",
73
- "--others",
74
- "--ignored",
75
- "--exclude-standard",
76
- ],
77
- // FIXME(sualeh): this is super sketchy and might totally break in like a bazillion ways. i dont like it.
78
- vec![
79
- "sh",
80
- "-c",
81
- "git submodule foreach --quiet 'git ls-files --others --ignored --exclude-standard | sed \"s|^|$path/|\"'",
82
- ],
83
- ];
84
-
85
- for command in commands {
86
- let output = Command::new(&command[0])
87
- .args(&command[1..])
88
- .current_dir(workspace_root_path)
89
- .output()?;
90
-
91
- if output.status.success() {
92
- let files = String::from_utf8(output.stdout)?
93
- .lines()
94
- .filter(|line| !line.is_empty())
95
- .map(|line| String::from(line))
96
- .collect::<HashSet<_>>();
97
-
98
- gitignored_files.extend(files);
99
- }
100
- }
101
-
102
- Ok(gitignored_files)
103
- }
104
-
105
- pub async fn is_git_ignored(
106
- workspace_root_path: &str,
107
- file_path: &str,
108
- ) -> Result<bool, anyhow::Error> {
109
- let output = tokio::process::Command::new("git")
110
- .args(&["check-ignore", file_path])
111
- .current_dir(workspace_root_path)
112
- .output()
113
- .await?;
114
-
115
- Ok(output.status.success())
116
- }
117
-
118
- pub async fn is_git_directory(
119
- workspace_root_path: &str,
120
- ) -> Result<bool, anyhow::Error> {
121
- let output = tokio::process::Command::new("git")
122
- .args(&["rev-parse", "--is-inside-work-tree"])
123
- .current_dir(workspace_root_path)
124
- .output()
125
- .await?;
126
-
127
- Ok(output.status.success())
128
- }
129
-
130
- #[cfg(test)]
131
- mod tests {
132
- use super::*;
133
- use std::fs::File;
134
- use std::io::Write;
135
-
136
- #[test]
137
- fn test_no_ignored_files() {
138
- let dir = tempfile::tempdir().unwrap();
139
- let gitignored_files =
140
- list_ignored_files_and_directories(dir.path().to_str().unwrap(), false)
141
- .unwrap();
142
- Command::new("git")
143
- .args(&["init"])
144
- .current_dir(dir.path())
145
- .output()
146
- .unwrap();
147
- assert_eq!(gitignored_files.len(), 0);
148
- }
149
-
150
- #[test]
151
- fn test_one_ignored_file() {
152
- let dir = tempfile::tempdir().unwrap();
153
- println!("tempdir: {:?}", dir);
154
- let file_path = dir.path().join("ignored.txt");
155
- let mut file = File::create(&file_path).unwrap();
156
- writeln!(file, "This is an ignored file.").unwrap();
157
-
158
- let gitignore_path = dir.path().join(".gitignore");
159
- let mut gitignore = File::create(&gitignore_path).unwrap();
160
- writeln!(gitignore, "ignored.txt").unwrap();
161
-
162
- Command::new("git")
163
- .args(&["init"])
164
- .current_dir(dir.path())
165
- .output()
166
- .unwrap();
167
- let gitignored_files =
168
- list_ignored_files_and_directories(dir.path().to_str().unwrap(), false)
169
- .unwrap();
170
- println!(
171
- "ignored files for test_one_ignored_file: {:?}",
172
- gitignored_files
173
- );
174
- // assert_eq!(gitignored_files.len(), 1);
175
- assert!(gitignored_files.contains(&String::from("ignored.txt")));
176
- }
177
-
178
- #[test]
179
- fn test_multiple_ignored_files() {
180
- let dir = tempfile::tempdir().unwrap();
181
- println!("tempdir: {:?}", dir);
182
- let file_path1 = dir.path().join("ignored1.txt");
183
- let file_path2 = dir.path().join("ignored2.txt");
184
- let mut file1 = File::create(&file_path1).unwrap();
185
- let mut file2 = File::create(&file_path2).unwrap();
186
- writeln!(file1, "This is an ignored file.").unwrap();
187
- writeln!(file2, "This is another ignored file.").unwrap();
188
-
189
- let gitignore_path = dir.path().join(".gitignore");
190
- let mut gitignore = File::create(&gitignore_path).unwrap();
191
- writeln!(gitignore, "*.txt").unwrap();
192
-
193
- Command::new("git")
194
- .args(&["init"])
195
- .current_dir(dir.path())
196
- .output()
197
- .unwrap();
198
- let gitignored_files =
199
- list_ignored_files_and_directories(dir.path().to_str().unwrap(), false)
200
- .unwrap();
201
- println!(
202
- "ignored files for test_multiple_ignored_files: {:?}",
203
- gitignored_files
204
- );
205
- // assert_eq!(gitignored_files.len(), 2);
206
- assert!(gitignored_files.contains(&String::from("ignored1.txt")));
207
- assert!(gitignored_files.contains(&String::from("ignored2.txt")));
208
- }
209
-
210
- #[test]
211
- fn test_git_submodule_ignored_files() {
212
- let dir = tempfile::tempdir().unwrap();
213
- let submodule_path = dir.path().join("submodule");
214
- std::fs::create_dir(&submodule_path).unwrap();
215
-
216
- let o = Command::new("git")
217
- .args(&["init"])
218
- .current_dir(&submodule_path)
219
- .output()
220
- .unwrap();
221
- println!("git init output: {:?}", o);
222
-
223
- let file_path = submodule_path.join("ignored.txt");
224
- let mut file = File::create(&file_path).unwrap();
225
- writeln!(file, "This is an ignored file.").unwrap();
226
-
227
- let file2 = submodule_path.join("ignored2.txt");
228
- let mut file = File::create(&file2).unwrap();
229
- writeln!(file, "This is another ignored file.").unwrap();
230
-
231
- let gitignore_path = submodule_path.join(".gitignore");
232
- let mut gitignore = File::create(&gitignore_path).unwrap();
233
- writeln!(gitignore, "*.txt").unwrap();
234
-
235
- let o = Command::new("git")
236
- .args(&["init"])
237
- .current_dir(dir.path())
238
- .output()
239
- .unwrap();
240
- println!("git init output: {:?}", o);
241
-
242
- // make a commit in the submodule
243
- let o = Command::new("git")
244
- .args(&["add", "."])
245
- .current_dir(&submodule_path)
246
- .output()
247
- .unwrap();
248
- println!("git add output: {:?}", o);
249
- let o = Command::new("git")
250
- .args(&["commit", "-m", "initial commit"])
251
- .current_dir(&submodule_path)
252
- .output()
253
- .unwrap();
254
- println!("git commit output: {:?}", o);
255
-
256
- let o = Command::new("git")
257
- .args(&["submodule", "add", "./submodule"])
258
- .current_dir(dir.path())
259
- .output()
260
- .unwrap();
261
- println!("git submodule add output: {:?}", o);
262
-
263
- let gitignored_files =
264
- list_ignored_files_and_directories(dir.path().to_str().unwrap(), false)
265
- .unwrap();
266
- println!(
267
- "ignored files for test_git_submodule_ignored_files: {:?}",
268
- gitignored_files
269
- );
270
- assert!(gitignored_files.contains(&String::from("submodule/ignored.txt")));
271
- assert!(gitignored_files.contains(&String::from("submodule/ignored2.txt")));
272
- }
273
-
274
- #[test]
275
- fn test_multiple_ignored_files_in_current_dir() {
276
- let gitignored_files =
277
- list_ignored_files_and_directories(".", false).unwrap();
278
- assert!(gitignored_files.len() > 1);
279
-
280
- // print a sample of the ignored files
281
- let mut count = 0;
282
- for file in gitignored_files {
283
- println!("ignored file: {:?}", file);
284
- count += 1;
285
- if count > 10 {
286
- break;
287
- }
288
- }
289
- }
290
-
291
- #[tokio::test]
292
- async fn test_file_not_ignored() {
293
- let dir = tempfile::tempdir().unwrap();
294
- let file_path = dir.path().join("not_ignored.txt");
295
- let mut file = File::create(&file_path).unwrap();
296
- writeln!(file, "This is not an ignored file.").unwrap();
297
-
298
- Command::new("git")
299
- .args(&["init"])
300
- .current_dir(dir.path())
301
- .output()
302
- .unwrap();
303
- let is_ignored =
304
- is_git_ignored(dir.path().to_str().unwrap(), "not_ignored.txt")
305
- .await
306
- .unwrap();
307
- assert_eq!(is_ignored, false);
308
- }
309
-
310
- #[tokio::test]
311
- async fn test_file_ignored() {
312
- let dir = tempfile::tempdir().unwrap();
313
- let file_path = dir.path().join("ignored.txt");
314
- let mut file = File::create(&file_path).unwrap();
315
- writeln!(file, "This is an ignored file.").unwrap();
316
-
317
- let gitignore_path = dir.path().join(".gitignore");
318
- let mut gitignore = File::create(&gitignore_path).unwrap();
319
- writeln!(gitignore, "ignored.txt").unwrap();
320
-
321
- Command::new("git")
322
- .args(&["init"])
323
- .current_dir(dir.path())
324
- .output()
325
- .unwrap();
326
- let is_ignored =
327
- is_git_ignored(dir.path().to_str().unwrap(), "ignored.txt")
328
- .await
329
- .unwrap();
330
- assert_eq!(is_ignored, true);
331
- }
332
-
333
- #[tokio::test]
334
- async fn test_file_ignored_with_wildcard() {
335
- let dir = tempfile::tempdir().unwrap();
336
- let file_path = dir.path().join("ignored.txt");
337
- let mut file = File::create(&file_path).unwrap();
338
- writeln!(file, "This is an ignored file.").unwrap();
339
-
340
- let gitignore_path = dir.path().join(".gitignore");
341
- let mut gitignore = File::create(&gitignore_path).unwrap();
342
- writeln!(gitignore, "*.txt").unwrap();
343
-
344
- Command::new("git")
345
- .args(&["init"])
346
- .current_dir(dir.path())
347
- .output()
348
- .unwrap();
349
- let is_ignored =
350
- is_git_ignored(dir.path().to_str().unwrap(), "ignored.txt")
351
- .await
352
- .unwrap();
353
- assert_eq!(is_ignored, true);
354
- }
355
- }
package/src/lib.rs DELETED
@@ -1,300 +0,0 @@
1
- #![windows_subsystem = "windows"]
2
- #![deny(clippy::all)]
3
- #![deny(unsafe_op_in_unsafe_fn)]
4
- pub mod file_utils;
5
- pub mod logger;
6
- pub mod merkle_tree;
7
-
8
- use std::{collections::HashSet, vec};
9
-
10
- use anyhow::Context;
11
- use merkle_tree::{LocalConstruction, MerkleTree};
12
- use tracing::{debug, info};
13
-
14
- #[macro_use]
15
- extern crate napi_derive;
16
-
17
- #[napi]
18
- pub struct MerkleClient {
19
- tree: MerkleTree,
20
- absolute_root_directory: String,
21
- _guard: Option<logger::GuardType>,
22
- }
23
-
24
- #[napi]
25
- impl MerkleClient {
26
- #[napi(constructor)]
27
- pub fn new(absolute_root_directory: String) -> MerkleClient {
28
- let _guard = logger::init_logger();
29
-
30
- // let canonical_root_directory = std::path::Path::new(&absolute_root_directory);
31
- // use dunce::canonicalize;
32
- // let canonical_root_directory = match dunce::canonicalize(&canonical_root_directory) {
33
- // Ok(path) => path.to_str().unwrap_or(&absolute_root_directory).to_string().to_lowercase(),
34
- // Err(e) => {
35
- // info!("Error in canonicalizing path: path: {:?}, error {:?}", canonical_root_directory, e);
36
- // absolute_root_directory
37
- // }
38
- // };
39
-
40
- MerkleClient {
41
- tree: MerkleTree::empty_tree(),
42
- absolute_root_directory,
43
- _guard,
44
- }
45
- }
46
-
47
- #[napi]
48
- pub async unsafe fn init(
49
- &mut self,
50
- git_ignored_files: Vec<String>,
51
- is_git_repo: bool,
52
- ) -> Result<(), napi::Error> {
53
- // 1. compute the merkle tree
54
- // 2. update the backend
55
- // 3. sync with the remote
56
- info!("Merkle tree compute started!");
57
- info!("Root directory: {:?}", self.absolute_root_directory);
58
- unsafe {
59
- self
60
- .compute_merkle_tree(git_ignored_files, is_git_repo)
61
- .await?;
62
- }
63
-
64
- Ok(())
65
- }
66
-
67
- pub async unsafe fn interrupt(&mut self) -> Result<(), napi::Error> {
68
- unimplemented!("Interrupt is not implemented yet");
69
- }
70
-
71
- #[napi]
72
- pub async unsafe fn compute_merkle_tree(
73
- &mut self,
74
- git_ignored_files: Vec<String>,
75
- is_git_repo: bool,
76
- ) -> Result<(), napi::Error> {
77
- // make the git ignored files into a hash set
78
- let mut git_ignored_set = HashSet::from_iter(git_ignored_files.into_iter());
79
-
80
- // if the hashset itself contains the root directory, then we should remove it.
81
- // this is because the root directory is not a file, and we don't want to ignore it.
82
- if git_ignored_set.contains(&self.absolute_root_directory) {
83
- git_ignored_set.remove(&self.absolute_root_directory);
84
- }
85
-
86
- let t = MerkleTree::construct_merkle_tree(
87
- self.absolute_root_directory.clone(),
88
- git_ignored_set,
89
- is_git_repo,
90
- )
91
- .await;
92
-
93
- match t {
94
- Ok(tree) => {
95
- self.tree = tree;
96
- Ok(())
97
- }
98
- Err(e) => Err(napi::Error::new(
99
- napi::Status::Unknown,
100
- format!("Error in compute_merkle_tree: {:?}", e),
101
- )),
102
- }
103
- }
104
-
105
- #[napi]
106
- pub async unsafe fn update_file(&mut self, file_path: String) {
107
- let _ = self.tree.update_file(file_path).await;
108
- }
109
-
110
- #[napi]
111
- pub async unsafe fn delete_file(&mut self, file_path: String) {
112
- let _r = self.tree.delete_file(file_path);
113
- }
114
-
115
- #[napi]
116
- pub async fn get_subtree_hash(
117
- &self,
118
- relative_path: String,
119
- ) -> Result<String, napi::Error> {
120
- debug!("get_subtree_hash: relative_path: {:?}", relative_path);
121
-
122
- let relative_path_without_leading_slash = match relative_path
123
- .strip_prefix('.')
124
- {
125
- Some(path) => path.strip_prefix(std::path::MAIN_SEPARATOR).unwrap_or(""),
126
- None => relative_path.as_str(),
127
- };
128
- debug!(
129
- "relative_path_without_leading_slash: {:?}",
130
- relative_path_without_leading_slash
131
- );
132
-
133
- let absolute_path = if !relative_path_without_leading_slash.is_empty() {
134
- std::path::Path::new(&self.absolute_root_directory)
135
- .join(relative_path_without_leading_slash)
136
- } else {
137
- std::path::Path::new(&self.absolute_root_directory).to_path_buf()
138
- };
139
-
140
- debug!("absolute_path: {:?}", absolute_path);
141
-
142
- let absolute_path_string = match absolute_path.to_str() {
143
- Some(path) => path.to_string(),
144
- None => {
145
- return Err(napi::Error::new(
146
- napi::Status::Unknown,
147
- format!("some string error"),
148
- ))
149
- }
150
- };
151
-
152
- debug!("absolute_path_string: {:?}", absolute_path_string);
153
-
154
- let hash = self
155
- .tree
156
- .get_subtree_hash(absolute_path_string.as_str())
157
- .await;
158
-
159
- match hash {
160
- Ok(hash) => Ok(hash),
161
- Err(e) => Err(napi::Error::new(
162
- napi::Status::Unknown,
163
- format!("Error in get_subtree_hash. \nRelative path: {:?}, \nAbsolute path: {:?}, \nRoot directory: {:?}\nError: {:?}", &relative_path, absolute_path, self.absolute_root_directory, e)
164
- )),
165
- }
166
- }
167
-
168
- #[napi]
169
- pub async fn get_num_embeddable_files(&self) -> Result<i32, napi::Error> {
170
- let num = self.tree.get_num_embeddable_files().await;
171
-
172
- match num {
173
- Ok(num) => Ok(num),
174
- Err(e) => Err(napi::Error::new(
175
- napi::Status::Unknown,
176
- format!("Error in get_num_embeddable_files: {:?}", e),
177
- )),
178
- }
179
- }
180
-
181
- pub async fn get_num_embeddable_files_in_subtree(
182
- &self,
183
- relative_path: String,
184
- ) -> Result<i32, napi::Error> {
185
- let absolute_path = std::path::Path::new(&self.absolute_root_directory)
186
- .join(relative_path)
187
- .canonicalize()?;
188
-
189
- let num = self
190
- .tree
191
- .get_num_embeddable_files_in_subtree(absolute_path)
192
- .await;
193
-
194
- match num {
195
- Ok(num) => Ok(num),
196
- Err(e) => Err(napi::Error::new(
197
- napi::Status::Unknown,
198
- format!("Error in get_num_embeddable_files_in_subtree: {:?}", e),
199
- )),
200
- }
201
- }
202
-
203
- #[napi]
204
- pub async fn get_all_files(&self) -> Result<Vec<String>, napi::Error> {
205
- let files = self.tree.get_all_files().await;
206
-
207
- match files {
208
- Ok(files) => Ok(files),
209
- Err(e) => Err(napi::Error::new(
210
- napi::Status::Unknown,
211
- format!("Error in get_all_files: {:?}", e),
212
- )),
213
- }
214
- }
215
-
216
- #[napi]
217
- pub async fn get_all_dir_files_to_embed(
218
- &self,
219
- absolute_file_path: String,
220
- ) -> Result<Vec<String>, napi::Error> {
221
- // let absolute_path = absolute_file_path.to_lowercase();
222
- // let absolute_path_str = absolute_path.as_str();
223
-
224
- let files = self
225
- .tree
226
- .get_all_dir_files_to_embed(absolute_file_path.as_str())
227
- .await;
228
-
229
- match files {
230
- Ok(files) => Ok(files),
231
- Err(e) => Err(napi::Error::new(
232
- napi::Status::Unknown,
233
- format!("Error in get_all_dir_files_to_embed: {:?}", e),
234
- )),
235
- }
236
- }
237
-
238
- #[napi]
239
- pub async unsafe fn get_next_file_to_embed(
240
- &mut self,
241
- ) -> Result<Vec<String>, napi::Error> {
242
- let n = self.tree.get_next_file_to_embed().await;
243
-
244
- match n {
245
- Ok((file, path)) => {
246
- // now our job is to put the filename as the first element of the path.
247
-
248
- // TODO(sualeh): we should assert that the path is ascending up to the path.
249
-
250
- let ret = vec![file];
251
- let ret = ret.into_iter().chain(path.into_iter()).collect::<Vec<_>>();
252
- Ok(ret)
253
- }
254
- Err(e) => Err(napi::Error::new(
255
- napi::Status::Unknown,
256
- format!("Error in get_next_file_to_embed: {:?}", e),
257
- )),
258
- }
259
- }
260
-
261
- // FIXME(sualeh): get_spline
262
- #[napi]
263
- pub async fn get_spline(
264
- &self,
265
- absolute_file_path: String,
266
- ) -> Result<Vec<String>, napi::Error> {
267
- // let absolute_path = absolute_file_path.to_lowercase();
268
- // let absolute_path_str = absolute_path.as_str();
269
- let spline = self.tree.get_spline(absolute_file_path.as_str()).await;
270
-
271
- match spline {
272
- Ok(spline) => Ok(spline),
273
- Err(e) => Err(napi::Error::new(
274
- napi::Status::Unknown,
275
- format!("Error in get_spline: {:?}", e),
276
- )),
277
- }
278
- }
279
-
280
- #[napi]
281
- pub async fn get_hashes_for_files(
282
- &self,
283
- files: Vec<String>,
284
- ) -> Result<Vec<String>, napi::Error> {
285
- let hashes = self.tree.get_hashes_for_files(files).await;
286
-
287
- match hashes {
288
- Ok(hashes) => Ok(hashes),
289
- Err(e) => Err(napi::Error::new(
290
- napi::Status::Unknown,
291
- format!("Error in get_hashes_for_files: {:?}", e),
292
- )),
293
- }
294
- }
295
-
296
- #[napi]
297
- pub fn update_root_directory(&mut self, root_directory: String) {
298
- self.absolute_root_directory = root_directory;
299
- }
300
- }