@anysphere/file-service 0.0.0-c0e75c6f → 0.0.0-c8c4f8fc

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 CHANGED
@@ -17,6 +17,11 @@ tempfile = "3.8.0"
17
17
  anyhow = "1.0.75"
18
18
  tonic = "0.9.2"
19
19
  prost = "0.11.9"
20
+ tracing = "0.1.37"
21
+ tracing-subscriber = "0.3.17"
22
+ tracing-appender = "0.2.2"
23
+ binaryornot = "1.0.0"
24
+ dunce = "1.0.1"
20
25
 
21
26
  [build-dependencies]
22
27
  napi-build = "2.0.1"
package/index.d.ts CHANGED
@@ -4,13 +4,17 @@
4
4
  /* auto-generated by NAPI-RS */
5
5
 
6
6
  export class MerkleClient {
7
- constructor(rootDirectory: string)
7
+ constructor(absoluteRootDirectory: string)
8
8
  init(): Promise<void>
9
+ computeMerkleTree(): Promise<void>
9
10
  updateFile(filePath: string): Promise<void>
10
11
  deleteFile(filePath: string): Promise<void>
11
- getSubtreeHash(path: string): Promise<string>
12
+ getSubtreeHash(relativePath: string): Promise<string>
12
13
  getNumEmbeddableFiles(): Promise<number>
13
14
  getAllFiles(): Promise<Array<string>>
15
+ getAllDirFilesToEmbed(absoluteFilePath: string): Promise<Array<string>>
14
16
  getNextFileToEmbed(): Promise<Array<string>>
17
+ getSpline(absoluteFilePath: string): Promise<Array<string>>
15
18
  getHashesForFiles(files: Array<string>): Promise<Array<string>>
19
+ updateRootDirectory(rootDirectory: string): void
16
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anysphere/file-service",
3
- "version": "0.0.0-c0e75c6f",
3
+ "version": "0.0.0-c8c4f8fc",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "napi": {
@@ -9,7 +9,8 @@
9
9
  "additional": [
10
10
  "aarch64-apple-darwin",
11
11
  "aarch64-pc-windows-msvc",
12
- "universal-apple-darwin"
12
+ "universal-apple-darwin",
13
+ "aarch64-unknown-linux-gnu"
13
14
  ]
14
15
  }
15
16
  },
@@ -35,11 +36,12 @@
35
36
  "version": "napi version"
36
37
  },
37
38
  "optionalDependencies": {
38
- "@anysphere/file-service-win32-x64-msvc": "0.0.0-c0e75c6f",
39
- "@anysphere/file-service-darwin-x64": "0.0.0-c0e75c6f",
40
- "@anysphere/file-service-linux-x64-gnu": "0.0.0-c0e75c6f",
41
- "@anysphere/file-service-darwin-arm64": "0.0.0-c0e75c6f",
42
- "@anysphere/file-service-win32-arm64-msvc": "0.0.0-c0e75c6f",
43
- "@anysphere/file-service-darwin-universal": "0.0.0-c0e75c6f"
39
+ "@anysphere/file-service-win32-x64-msvc": "0.0.0-c8c4f8fc",
40
+ "@anysphere/file-service-darwin-x64": "0.0.0-c8c4f8fc",
41
+ "@anysphere/file-service-linux-x64-gnu": "0.0.0-c8c4f8fc",
42
+ "@anysphere/file-service-darwin-arm64": "0.0.0-c8c4f8fc",
43
+ "@anysphere/file-service-win32-arm64-msvc": "0.0.0-c8c4f8fc",
44
+ "@anysphere/file-service-darwin-universal": "0.0.0-c8c4f8fc",
45
+ "@anysphere/file-service-linux-arm64-gnu": "0.0.0-c8c4f8fc"
44
46
  }
45
47
  }
package/src/file_utils.rs CHANGED
@@ -12,9 +12,8 @@ pub fn is_in_bad_dir(file_path: &Path) -> Result<bool, Error> {
12
12
  let item_path = file_path
13
13
  .to_str()
14
14
  .ok_or(anyhow::anyhow!("Failed to convert path to string"))?;
15
- let is_bad_dir = (item_path.contains("node_modules")
16
- || item_path.contains(".git"))
17
- && !(item_path.ends_with(".git") || item_path.ends_with("node_modules"));
15
+ let is_bad_dir =
16
+ item_path.contains("node_modules") || item_path.contains(".git");
18
17
  Ok(is_bad_dir)
19
18
  }
20
19
 
@@ -38,14 +37,14 @@ pub fn is_good_file(file_path: &Path) -> Result<(), Error> {
38
37
 
39
38
  match file_name {
40
39
  "package-lock.json" | "pnpm-lock.yaml" | "yarn.lock" | "composer.lock"
41
- | "Gemfile.lock" => {
40
+ | "Gemfile.lock" | "bun.lockb" => {
42
41
  return Err(anyhow::anyhow!("File is just a lock file"));
43
42
  }
44
43
  _ => {}
45
44
  }
46
45
 
47
46
  match extension {
48
- "lock" | "bak" | "tmp" | "bin" | "exe" | "dll" | "so" => {
47
+ "lock" | "bak" | "tmp" | "bin" | "exe" | "dll" | "so" | "lockb" => {
49
48
  return Err(anyhow::anyhow!("File is just a lock file"));
50
49
  }
51
50
  _ => {}
@@ -63,7 +62,7 @@ pub fn is_good_file(file_path: &Path) -> Result<(), Error> {
63
62
  Some(extension) => match extension.to_str() {
64
63
  Some(ext_str) => {
65
64
  if bad_extensions.contains(&ext_str) {
66
- return Err(anyhow::anyhow!("File is not a valid UTF-8 string"));
65
+ return Err(anyhow::anyhow!("Binary file excluded from indexing."));
67
66
  }
68
67
  }
69
68
  None => {
@@ -89,10 +88,12 @@ pub fn is_good_file(file_path: &Path) -> Result<(), Error> {
89
88
  Ok(())
90
89
  }
91
90
 
91
+ // use binaryornot::is_binary;
92
+ // use anyhow::Context;
92
93
  // implement the buffer above:
93
94
  pub async fn is_good_file_runtime_check(
94
95
  file_path: &Path,
95
- buffer: &[u8],
96
+ _buffer: &[u8],
96
97
  ) -> Result<(), Error> {
97
98
  match get_file_size(file_path).await {
98
99
  Ok(size) if size > 2 * 1024 * 1024 => {
@@ -102,13 +103,10 @@ pub async fn is_good_file_runtime_check(
102
103
  _ => {}
103
104
  }
104
105
 
105
- for &byte in buffer.iter().take(2048) {
106
- if byte.is_ascii() {
107
- continue;
108
- } else {
109
- return Err(anyhow::anyhow!("File is not a valid UTF-8 string"));
110
- }
111
- }
106
+ // if is_binary(file_path).context("Failed to check if file is binary")? {
107
+ // return Err(anyhow::anyhow!("File is binary"));
108
+ // }
109
+
112
110
  Ok(())
113
111
  }
114
112
 
package/src/git_utils.rs CHANGED
@@ -1,10 +1,10 @@
1
1
  use std::collections::HashSet;
2
- use std::path::PathBuf;
3
2
  use std::process::Command;
4
3
 
5
4
  pub fn list_ignored_files(
6
5
  workspace_root_path: &str,
7
- ) -> Result<HashSet<PathBuf>, Box<dyn std::error::Error>> {
6
+ should_return_absolute_paths: bool,
7
+ ) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
8
8
  let mut gitignored_files = HashSet::new();
9
9
 
10
10
  let commands = vec![
@@ -15,12 +15,66 @@ pub fn list_ignored_files(
15
15
  "--ignored",
16
16
  "--exclude-standard",
17
17
  ],
18
+ // FIXME(sualeh): this is super sketchy and might totally break in like a bazillion ways. i dont like it.
19
+ vec![
20
+ "sh",
21
+ "-c",
22
+ "git submodule foreach --quiet 'git ls-files --others --ignored --exclude-standard | sed \"s|^|$path/|\"'",
23
+ ],
24
+ ];
25
+
26
+ for command in commands {
27
+ let output = Command::new(&command[0])
28
+ .args(&command[1..])
29
+ .current_dir(workspace_root_path)
30
+ .output()?;
31
+
32
+ if output.status.success() {
33
+ let files = String::from_utf8(output.stdout)?
34
+ .lines()
35
+ .filter(|line| !line.is_empty())
36
+ .map(|line| {
37
+ if should_return_absolute_paths {
38
+ let mut path = std::path::PathBuf::from(workspace_root_path);
39
+ path.push(line);
40
+
41
+ match path.canonicalize() {
42
+ Ok(canonical_path) => {
43
+ canonical_path.to_string_lossy().into_owned()
44
+ }
45
+ Err(_) => String::from(line),
46
+ }
47
+ } else {
48
+ String::from(line)
49
+ }
50
+ })
51
+ .collect::<HashSet<_>>();
52
+
53
+ gitignored_files.extend(files);
54
+ }
55
+ }
56
+
57
+ Ok(gitignored_files)
58
+ }
59
+
60
+ pub fn list_ignored_files_with_absolute_paths(
61
+ workspace_root_path: &str,
62
+ ) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
63
+ let mut gitignored_files = HashSet::new();
64
+
65
+ let commands = vec![
18
66
  vec![
19
67
  "git",
20
- "submodule",
21
- "foreach",
22
- "--quiet",
23
- "git ls-files --others --ignored --exclude-standard | sed 's|^|$path/|'",
68
+ "ls-files",
69
+ "--others",
70
+ "--ignored",
71
+ "--exclude-standard",
72
+ ],
73
+ // FIXME(sualeh): this is super sketchy and might totally break in like a bazillion ways. i dont like it.
74
+ vec![
75
+ "sh",
76
+ "-c",
77
+ "git submodule foreach --quiet 'git ls-files --others --ignored --exclude-standard | sed \"s|^|$path/|\"'",
24
78
  ],
25
79
  ];
26
80
 
@@ -34,7 +88,7 @@ pub fn list_ignored_files(
34
88
  let files = String::from_utf8(output.stdout)?
35
89
  .lines()
36
90
  .filter(|line| !line.is_empty())
37
- .map(|line| PathBuf::from(line))
91
+ .map(|line| String::from(line))
38
92
  .collect::<HashSet<_>>();
39
93
 
40
94
  gitignored_files.extend(files);
@@ -57,6 +111,18 @@ pub async fn is_git_ignored(
57
111
  Ok(output.status.success())
58
112
  }
59
113
 
114
+ pub async fn is_git_directory(
115
+ workspace_root_path: &str,
116
+ ) -> Result<bool, anyhow::Error> {
117
+ let output = tokio::process::Command::new("git")
118
+ .args(&["rev-parse", "--is-inside-work-tree"])
119
+ .current_dir(workspace_root_path)
120
+ .output()
121
+ .await?;
122
+
123
+ Ok(output.status.success())
124
+ }
125
+
60
126
  #[cfg(test)]
61
127
  mod tests {
62
128
  use super::*;
@@ -66,7 +132,8 @@ mod tests {
66
132
  #[test]
67
133
  fn test_no_ignored_files() {
68
134
  let dir = tempfile::tempdir().unwrap();
69
- let gitignored_files = list_ignored_files(dir.path().to_str().unwrap()).unwrap();
135
+ let gitignored_files =
136
+ list_ignored_files(dir.path().to_str().unwrap(), false).unwrap();
70
137
  Command::new("git")
71
138
  .args(&["init"])
72
139
  .current_dir(dir.path())
@@ -92,13 +159,14 @@ mod tests {
92
159
  .current_dir(dir.path())
93
160
  .output()
94
161
  .unwrap();
95
- let gitignored_files = list_ignored_files(dir.path().to_str().unwrap()).unwrap();
162
+ let gitignored_files =
163
+ list_ignored_files(dir.path().to_str().unwrap(), false).unwrap();
96
164
  println!(
97
165
  "ignored files for test_one_ignored_file: {:?}",
98
166
  gitignored_files
99
167
  );
100
168
  // assert_eq!(gitignored_files.len(), 1);
101
- assert!(gitignored_files.contains(&PathBuf::from("ignored.txt")));
169
+ assert!(gitignored_files.contains(&String::from("ignored.txt")));
102
170
  }
103
171
 
104
172
  #[test]
@@ -121,19 +189,83 @@ mod tests {
121
189
  .current_dir(dir.path())
122
190
  .output()
123
191
  .unwrap();
124
- let gitignored_files = list_ignored_files(dir.path().to_str().unwrap()).unwrap();
192
+ let gitignored_files =
193
+ list_ignored_files(dir.path().to_str().unwrap(), false).unwrap();
125
194
  println!(
126
195
  "ignored files for test_multiple_ignored_files: {:?}",
127
196
  gitignored_files
128
197
  );
129
198
  // assert_eq!(gitignored_files.len(), 2);
130
- assert!(gitignored_files.contains(&PathBuf::from("ignored1.txt")));
131
- assert!(gitignored_files.contains(&PathBuf::from("ignored2.txt")));
199
+ assert!(gitignored_files.contains(&String::from("ignored1.txt")));
200
+ assert!(gitignored_files.contains(&String::from("ignored2.txt")));
201
+ }
202
+
203
+ #[test]
204
+ fn test_git_submodule_ignored_files() {
205
+ let dir = tempfile::tempdir().unwrap();
206
+ let submodule_path = dir.path().join("submodule");
207
+ std::fs::create_dir(&submodule_path).unwrap();
208
+
209
+ let o = Command::new("git")
210
+ .args(&["init"])
211
+ .current_dir(&submodule_path)
212
+ .output()
213
+ .unwrap();
214
+ println!("git init output: {:?}", o);
215
+
216
+ let file_path = submodule_path.join("ignored.txt");
217
+ let mut file = File::create(&file_path).unwrap();
218
+ writeln!(file, "This is an ignored file.").unwrap();
219
+
220
+ let file2 = submodule_path.join("ignored2.txt");
221
+ let mut file = File::create(&file2).unwrap();
222
+ writeln!(file, "This is another ignored file.").unwrap();
223
+
224
+ let gitignore_path = submodule_path.join(".gitignore");
225
+ let mut gitignore = File::create(&gitignore_path).unwrap();
226
+ writeln!(gitignore, "*.txt").unwrap();
227
+
228
+ let o = Command::new("git")
229
+ .args(&["init"])
230
+ .current_dir(dir.path())
231
+ .output()
232
+ .unwrap();
233
+ println!("git init output: {:?}", o);
234
+
235
+ // make a commit in the submodule
236
+ let o = Command::new("git")
237
+ .args(&["add", "."])
238
+ .current_dir(&submodule_path)
239
+ .output()
240
+ .unwrap();
241
+ println!("git add output: {:?}", o);
242
+ let o = Command::new("git")
243
+ .args(&["commit", "-m", "initial commit"])
244
+ .current_dir(&submodule_path)
245
+ .output()
246
+ .unwrap();
247
+ println!("git commit output: {:?}", o);
248
+
249
+ let o = Command::new("git")
250
+ .args(&["submodule", "add", "./submodule"])
251
+ .current_dir(dir.path())
252
+ .output()
253
+ .unwrap();
254
+ println!("git submodule add output: {:?}", o);
255
+
256
+ let gitignored_files =
257
+ list_ignored_files(dir.path().to_str().unwrap(), false).unwrap();
258
+ println!(
259
+ "ignored files for test_git_submodule_ignored_files: {:?}",
260
+ gitignored_files
261
+ );
262
+ assert!(gitignored_files.contains(&String::from("submodule/ignored.txt")));
263
+ assert!(gitignored_files.contains(&String::from("submodule/ignored2.txt")));
132
264
  }
133
265
 
134
266
  #[test]
135
267
  fn test_multiple_ignored_files_in_current_dir() {
136
- let gitignored_files = list_ignored_files(".").unwrap();
268
+ let gitignored_files = list_ignored_files(".", false).unwrap();
137
269
  assert!(gitignored_files.len() > 1);
138
270
 
139
271
  // print a sample of the ignored files
@@ -147,7 +279,6 @@ mod tests {
147
279
  }
148
280
  }
149
281
 
150
-
151
282
  #[tokio::test]
152
283
  async fn test_file_not_ignored() {
153
284
  let dir = tempfile::tempdir().unwrap();
@@ -160,7 +291,10 @@ mod tests {
160
291
  .current_dir(dir.path())
161
292
  .output()
162
293
  .unwrap();
163
- let is_ignored = is_git_ignored(dir.path().to_str().unwrap(), "not_ignored.txt").await.unwrap();
294
+ let is_ignored =
295
+ is_git_ignored(dir.path().to_str().unwrap(), "not_ignored.txt")
296
+ .await
297
+ .unwrap();
164
298
  assert_eq!(is_ignored, false);
165
299
  }
166
300
 
@@ -180,7 +314,10 @@ mod tests {
180
314
  .current_dir(dir.path())
181
315
  .output()
182
316
  .unwrap();
183
- let is_ignored = is_git_ignored(dir.path().to_str().unwrap(), "ignored.txt").await.unwrap();
317
+ let is_ignored =
318
+ is_git_ignored(dir.path().to_str().unwrap(), "ignored.txt")
319
+ .await
320
+ .unwrap();
184
321
  assert_eq!(is_ignored, true);
185
322
  }
186
323
 
@@ -200,8 +337,10 @@ mod tests {
200
337
  .current_dir(dir.path())
201
338
  .output()
202
339
  .unwrap();
203
- let is_ignored = is_git_ignored(dir.path().to_str().unwrap(), "ignored.txt").await.unwrap();
340
+ let is_ignored =
341
+ is_git_ignored(dir.path().to_str().unwrap(), "ignored.txt")
342
+ .await
343
+ .unwrap();
204
344
  assert_eq!(is_ignored, true);
205
345
  }
206
-
207
346
  }
package/src/lib.rs CHANGED
@@ -1,4 +1,5 @@
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;
@@ -6,6 +7,9 @@ pub mod merkle_tree;
6
7
  use std::vec;
7
8
 
8
9
  use merkle_tree::{LocalConstruction, MerkleTree};
10
+ use tracing::{info, Level};
11
+ use tracing_appender::rolling::{RollingFileAppender, Rotation};
12
+ use tracing_subscriber::fmt;
9
13
 
10
14
  #[macro_use]
11
15
  extern crate napi_derive;
@@ -13,16 +17,46 @@ extern crate napi_derive;
13
17
  #[napi]
14
18
  pub struct MerkleClient {
15
19
  tree: MerkleTree,
16
- root_directory: String,
20
+ absolute_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
17
38
  }
18
39
 
19
40
  #[napi]
20
41
  impl MerkleClient {
21
42
  #[napi(constructor)]
22
- pub fn new(root_directory: String) -> MerkleClient {
43
+ pub fn new(absolute_root_directory: String) -> MerkleClient {
44
+ let _guard = init_logger();
45
+
46
+ let canonical_root_directory = std::path::Path::new(&absolute_root_directory);
47
+ // use dunce::canonicalize;
48
+ let canonical_root_directory = match dunce::canonicalize(&canonical_root_directory) {
49
+ Ok(path) => path.to_str().unwrap_or(&absolute_root_directory).to_string(),
50
+ Err(e) => {
51
+ info!("Error in canonicalizing path: path: {:?}, error {:?}", canonical_root_directory, e);
52
+ absolute_root_directory
53
+ }
54
+ };
55
+
23
56
  MerkleClient {
24
57
  tree: MerkleTree::empty_tree(),
25
- root_directory,
58
+ absolute_root_directory: canonical_root_directory,
59
+ _guard,
26
60
  }
27
61
  }
28
62
 
@@ -31,7 +65,10 @@ impl MerkleClient {
31
65
  // 1. compute the merkle tree
32
66
  // 2. update the backend
33
67
  // 3. sync with the remote
34
- self.compute_merkle_tree().await?;
68
+ info!("Merkle tree compute started!");
69
+ unsafe {
70
+ self.compute_merkle_tree().await?;
71
+ }
35
72
 
36
73
  Ok(())
37
74
  }
@@ -40,12 +77,12 @@ impl MerkleClient {
40
77
  unimplemented!("Interrupt is not implemented yet");
41
78
  }
42
79
 
43
- // #[napi]
80
+ #[napi]
44
81
  pub async unsafe fn compute_merkle_tree(
45
82
  &mut self,
46
83
  ) -> Result<(), napi::Error> {
47
84
  let t =
48
- MerkleTree::construct_merkle_tree(self.root_directory.clone()).await;
85
+ MerkleTree::construct_merkle_tree(self.absolute_root_directory.clone()).await;
49
86
 
50
87
  match t {
51
88
  Ok(tree) => {
@@ -72,15 +109,38 @@ impl MerkleClient {
72
109
  #[napi]
73
110
  pub async fn get_subtree_hash(
74
111
  &self,
75
- path: String,
112
+ relative_path: String,
76
113
  ) -> Result<String, napi::Error> {
77
- let hash = self.tree.get_subtree_hash(path).await;
114
+ let absolute_path =
115
+ std::path::Path::new(&self.absolute_root_directory).join(&relative_path);
116
+
117
+ let canonical_path = match dunce::canonicalize(&absolute_path) {
118
+ Ok(path) => path,
119
+ Err(e) => {
120
+ return Err(napi::Error::new(
121
+ napi::Status::Unknown,
122
+ format!("Error in canonicalizing path: {:?}", e),
123
+ ))
124
+ }
125
+ };
126
+
127
+ let cononical_str = match canonical_path.to_str() {
128
+ Some(s) => s,
129
+ None => {
130
+ return Err(napi::Error::new(
131
+ napi::Status::Unknown,
132
+ format!("Error in converting canonical path to string"),
133
+ ))
134
+ }
135
+ };
136
+
137
+ let hash = self.tree.get_subtree_hash(cononical_str).await;
78
138
 
79
139
  match hash {
80
140
  Ok(hash) => Ok(hash),
81
141
  Err(e) => Err(napi::Error::new(
82
142
  napi::Status::Unknown,
83
- format!("Error in get_subtree_hash: {:?}", e),
143
+ format!("Error in get_subtree_hash. \nRelative path: {:?}, \nAbsolute path: {:?}, \nCanonical path: {:?}, \nRoot directory: {:?}\nError: {:?}", &relative_path, absolute_path, canonical_path, self.absolute_root_directory, e),
84
144
  )),
85
145
  }
86
146
  }
@@ -98,6 +158,28 @@ impl MerkleClient {
98
158
  }
99
159
  }
100
160
 
161
+ pub async fn get_num_embeddable_files_in_subtree(
162
+ &self,
163
+ relative_path: String,
164
+ ) -> Result<i32, napi::Error> {
165
+ let absolute_path = std::path::Path::new(&self.absolute_root_directory)
166
+ .join(relative_path)
167
+ .canonicalize()?;
168
+
169
+ let num = self
170
+ .tree
171
+ .get_num_embeddable_files_in_subtree(absolute_path)
172
+ .await;
173
+
174
+ match num {
175
+ Ok(num) => Ok(num),
176
+ Err(e) => Err(napi::Error::new(
177
+ napi::Status::Unknown,
178
+ format!("Error in get_num_embeddable_files_in_subtree: {:?}", e),
179
+ )),
180
+ }
181
+ }
182
+
101
183
  #[napi]
102
184
  pub async fn get_all_files(&self) -> Result<Vec<String>, napi::Error> {
103
185
  let files = self.tree.get_all_files().await;
@@ -111,6 +193,26 @@ impl MerkleClient {
111
193
  }
112
194
  }
113
195
 
196
+ #[napi]
197
+ pub async fn get_all_dir_files_to_embed(
198
+ &self,
199
+ absolute_file_path: String,
200
+ ) -> Result<Vec<String>, napi::Error> {
201
+ let absolute_path_str = absolute_file_path.as_str();
202
+ let files = self
203
+ .tree
204
+ .get_all_dir_files_to_embed(absolute_path_str)
205
+ .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_dir_files_to_embed: {:?}", e),
212
+ )),
213
+ }
214
+ }
215
+
114
216
  #[napi]
115
217
  pub async unsafe fn get_next_file_to_embed(
116
218
  &mut self,
@@ -125,7 +227,6 @@ impl MerkleClient {
125
227
 
126
228
  let ret = vec![file];
127
229
  let ret = ret.into_iter().chain(path.into_iter()).collect::<Vec<_>>();
128
-
129
230
  Ok(ret)
130
231
  }
131
232
  Err(e) => Err(napi::Error::new(
@@ -135,6 +236,24 @@ impl MerkleClient {
135
236
  }
136
237
  }
137
238
 
239
+ // FIXME(sualeh): get_spline
240
+ #[napi]
241
+ pub async fn get_spline(
242
+ &self,
243
+ absolute_file_path: String,
244
+ ) -> Result<Vec<String>, napi::Error> {
245
+ let absolute_path_str = absolute_file_path.as_str();
246
+ let spline = self.tree.get_spline(absolute_path_str).await;
247
+
248
+ match spline {
249
+ Ok(spline) => Ok(spline),
250
+ Err(e) => Err(napi::Error::new(
251
+ napi::Status::Unknown,
252
+ format!("Error in get_spline: {:?}", e),
253
+ )),
254
+ }
255
+ }
256
+
138
257
  #[napi]
139
258
  pub async fn get_hashes_for_files(
140
259
  &self,
@@ -151,8 +270,8 @@ impl MerkleClient {
151
270
  }
152
271
  }
153
272
 
154
- // #[napi]
273
+ #[napi]
155
274
  pub fn update_root_directory(&mut self, root_directory: String) {
156
- self.root_directory = root_directory;
275
+ self.absolute_root_directory = root_directory;
157
276
  }
158
277
  }
@@ -1,10 +1,11 @@
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::path::PathBuf;
7
- use std::{collections::HashMap, path::Path, sync::Arc};
7
+ use std::collections::{BTreeMap, HashSet};
8
+ use std::path::{Path, PathBuf};
8
9
  use tonic::async_trait;
9
10
 
10
11
  #[async_trait]
@@ -28,32 +29,49 @@ impl LocalConstruction for MerkleTree {
28
29
  /// 3. construct merkle tree
29
30
  /// 4. return merkle tree
30
31
  async fn construct_merkle_tree(
31
- root_directory: String,
32
+ absolute_path_to_root_directory: String,
32
33
  ) -> Result<MerkleTree, anyhow::Error> {
33
- let path = PathBuf::from(root_directory.clone());
34
+ let path = PathBuf::from(absolute_path_to_root_directory.clone());
34
35
  if !path.exists() {
35
36
  // FIXME: we should report this via a good logger.
36
37
  panic!("Root directory does not exist!");
37
38
  }
38
39
 
39
- let root_node = MerkleNode::new(path, None).await;
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;
40
56
  let mut mt = MerkleTree {
41
57
  root: root_node,
42
- files: HashMap::new(),
43
- root_path: root_directory,
58
+ files: BTreeMap::new(),
59
+ root_path: absolute_path_to_root_directory,
44
60
  cursor: None,
61
+ git_ignored_files,
45
62
  };
46
63
 
47
64
  // we now iterate over all the nodes and add them to the hashmap
48
65
  // TODO(later): i can make this parallel.
49
66
  fn add_nodes_to_hashmap<'a>(
50
67
  node: &'a MerkleNodePtr,
51
- files: &'a mut HashMap<String, File>,
68
+ files: &'a mut BTreeMap<String, File>,
52
69
  ) -> PinnedFuture<'a, ()> {
53
70
  Box::pin(async move {
54
71
  let node_reader = node.read().await;
55
72
  match &node_reader.node_type {
56
73
  NodeType::Branch(n) => {
74
+ tracing::info!("Branch: {:?}", n.0);
57
75
  let children = &n.1;
58
76
  files.insert(n.0.clone(), File { node: node.clone() });
59
77
  for child in children {
@@ -62,7 +80,12 @@ impl LocalConstruction for MerkleTree {
62
80
  }
63
81
  NodeType::File(file_name) => {
64
82
  let f = File { node: node.clone() };
65
- files.insert(file_name.clone(), f);
83
+ let canonical_file_name = match dunce::canonicalize(file_name) {
84
+ Ok(path) => path.to_str().unwrap_or(file_name).to_string(),
85
+ Err(_) => file_name.clone(),
86
+ };
87
+
88
+ files.insert(canonical_file_name, f);
66
89
  }
67
90
  NodeType::ErrorNode(_) => {
68
91
  // do nothing
@@ -73,6 +96,9 @@ impl LocalConstruction for MerkleTree {
73
96
 
74
97
  add_nodes_to_hashmap(&mt.root, &mut mt.files).await;
75
98
 
99
+ tracing::info!("Merkle tree compute finished!");
100
+ tracing::info!("Merkle tree: {}", mt);
101
+
76
102
  Ok(mt)
77
103
  }
78
104
 
@@ -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::{collections::HashMap, fs, path::Path, sync::Arc};
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,8 +16,9 @@ pub type MerkleNodePtr = Arc<RwLock<MerkleNode>>;
12
16
  pub struct MerkleTree {
13
17
  root_path: String,
14
18
  root: MerkleNodePtr,
15
- files: HashMap<String, File>,
16
- cursor: Option<MerkleNodePtr>,
19
+ files: BTreeMap<String, File>,
20
+ cursor: Option<usize>,
21
+ git_ignored_files: HashSet<String>,
17
22
  }
18
23
 
19
24
  #[derive(Debug)]
@@ -87,21 +92,23 @@ impl MerkleTree {
87
92
  pub fn empty_tree() -> MerkleTree {
88
93
  MerkleTree {
89
94
  root: Arc::new(RwLock::new(MerkleNode::empty_node(None, None))),
90
- files: HashMap::new(),
95
+ files: BTreeMap::new(),
91
96
  root_path: "".to_string(),
92
97
  cursor: None,
98
+ git_ignored_files: HashSet::new(),
93
99
  }
94
100
  }
95
101
 
96
102
  pub async fn get_subtree_hash(
97
103
  &self,
98
- path: String,
104
+ absolute_path: &str,
99
105
  ) -> Result<String, anyhow::Error> {
100
- let path = PathBuf::from(path);
101
- let node = match self.files.get(path.to_str().unwrap()) {
106
+
107
+ let node = match self.files.get(absolute_path) {
102
108
  Some(file) => file.node.clone(),
103
109
  None => {
104
- return Err(anyhow::anyhow!("Could not find file in tree!"));
110
+ let all_files: Vec<String> = self.files.keys().cloned().collect();
111
+ return Err(anyhow::anyhow!("Could not find file in tree! Looking for: {}. All files: {:?}", absolute_path, all_files));
105
112
  }
106
113
  };
107
114
 
@@ -132,6 +139,43 @@ impl MerkleTree {
132
139
  Ok(count)
133
140
  }
134
141
 
142
+ pub async fn get_num_embeddable_files_in_subtree(
143
+ &self,
144
+ absolute_path: PathBuf,
145
+ ) -> Result<i32, anyhow::Error> {
146
+ let mut count = 0;
147
+
148
+ let absolute_path = match absolute_path.to_str() {
149
+ Some(s) => s.to_string(),
150
+ None => {
151
+ return Err(anyhow::anyhow!(
152
+ "get_num_embeddable_files_in_subtree: Failed to convert path to string"
153
+ ))
154
+ }
155
+ };
156
+
157
+ // TODO(sualeh): worth keeping this list sorted. its now a btree
158
+
159
+ for (_, file) in &self.files {
160
+ let file_reader = file.node.read().await;
161
+ match &file_reader.node_type {
162
+ NodeType::File(file_name) => {
163
+ if file_name.contains(&absolute_path) {
164
+ count += 1;
165
+ }
166
+ }
167
+ NodeType::Branch(_) => {
168
+ continue;
169
+ }
170
+ NodeType::ErrorNode(_) => {
171
+ continue;
172
+ }
173
+ }
174
+ }
175
+
176
+ Ok(count)
177
+ }
178
+
135
179
  pub async fn get_all_files(&self) -> Result<Vec<String>, anyhow::Error> {
136
180
  let mut files = Vec::new();
137
181
 
@@ -188,83 +232,119 @@ impl MerkleTree {
188
232
  pub async fn get_next_file_to_embed(
189
233
  &mut self,
190
234
  ) -> Result<(String, Vec<String>), anyhow::Error> {
191
- // the plan is to do an in-order traversal of the tree.
192
-
193
- // first the edge case to deal with:
194
- // cursor == None
195
- if self.cursor.is_none() {
196
- // If the root is a file, return its name.
197
- if let NodeType::File(file_path) = &self.root.read().await.node_type {
198
- return Ok((file_path.clone(), vec![]));
199
- }
200
-
201
- // if the path is not empty, we can iterate till we find the first child.
202
- let mut potential_first_child = self.root.clone();
203
- let mut is_branch = true;
204
- let mut path = Vec::new();
205
-
206
- while is_branch {
207
- let node = {
208
- let potential_first_child_reader = potential_first_child.read().await;
209
- match &potential_first_child_reader.node_type {
210
- NodeType::Branch(branch) => branch.clone(),
211
- NodeType::File(_) => {
212
- return Err(anyhow::anyhow!(
213
- "get_next_file_to_embed: This should not happen! the branch happened to be file."
214
- ));
215
- }
216
- NodeType::ErrorNode(_) => {
217
- return Err(anyhow::anyhow!("Cursor is an error node!"));
218
- }
219
- }
220
- };
235
+ // if the cursor is none, set it to 0
236
+ let cursor = match self.cursor {
237
+ Some(cursor) => cursor,
238
+ None => {
239
+ self.cursor = Some(0);
240
+ 0
241
+ }
242
+ };
221
243
 
222
- let current_node_name = &node.0;
223
- let child_list = &node.1;
244
+ // get the thing at the cursor. while we dont find a file, we keep incrementing the cursor.
245
+ let mut cursor = cursor;
246
+ loop {
247
+ // O(log n)
248
+ let file = match self.files.values().nth(cursor) {
249
+ Some(file) => file,
250
+ None => {
251
+ return Err(anyhow::anyhow!("Could not find file to embed!"));
252
+ }
253
+ };
224
254
 
225
- if let Some(c) = child_list.first() {
226
- let c_reader = c.read().await;
255
+ let file_reader = file.node.read().await;
256
+ match &file_reader.node_type {
257
+ NodeType::File(f) => {
258
+ // update the cursor.
259
+ self.cursor = Some(cursor + 1);
260
+ let spline = self.get_spline(f).await?;
261
+ return Ok((f.clone(), spline));
262
+ }
263
+ NodeType::Branch(_) => {
264
+ cursor += 1;
265
+ continue;
266
+ }
267
+ NodeType::ErrorNode(_) => {
268
+ cursor += 1;
269
+ continue;
270
+ }
271
+ }
272
+ }
273
+ }
227
274
 
228
- match &c_reader.node_type {
229
- NodeType::File(file_path) => {
230
- // must set the cursor!
231
- self.cursor = Some(c.clone());
275
+ pub async fn get_all_dir_files_to_embed(
276
+ &self,
277
+ absolute_path: &str,
278
+ ) -> Result<Vec<String>, anyhow::Error> {
279
+ let mut files = Vec::new();
232
280
 
233
- return Ok((file_path.clone(), path));
234
- }
235
- NodeType::Branch(_) => {
236
- potential_first_child = c.clone();
237
- is_branch = true;
281
+ for (file_path, f) in &self.files {
282
+ if !file_path.contains(absolute_path) {
283
+ continue;
284
+ }
238
285
 
239
- // add the path to the current node.
240
- path.push(current_node_name.clone());
241
- }
242
- NodeType::ErrorNode(_) => {
243
- return Err(anyhow::anyhow!("Cursor is an error node!"));
244
- }
245
- }
246
- } else {
247
- // If the root has no children, return an error.
248
- return Err(anyhow::anyhow!("Root has no children!"));
286
+ match f.node.read().await.node_type {
287
+ NodeType::File(_) => {
288
+ files.push(file_path.clone());
289
+ }
290
+ NodeType::Branch(_) => {
291
+ continue;
292
+ }
293
+ NodeType::ErrorNode(_) => {
294
+ continue;
249
295
  }
250
296
  }
251
297
  }
252
298
 
253
- // THE DEFAULT CASE:
254
- // we already have a cursor at a file.
255
-
256
- // UNWRAP checked and fine. see the none case above.
257
- let cursor_name = self.cursor.as_ref().unwrap();
258
- let cursor_reader = cursor_name.read().await;
299
+ Ok(files)
300
+ }
259
301
 
260
- // invariant: you must be a file!!
302
+ // TODO(sualeh): i need tests for this!!
303
+ pub async fn get_spline(
304
+ &self,
305
+ absolute_path: &str,
306
+ ) -> Result<Vec<String>, anyhow::Error> {
307
+ info!("get_spline called with absolute_path: {}", absolute_path);
308
+ let mut files = Vec::new();
261
309
 
262
- // everytime we get to a child list, we will add all the children to a fifo, and then pull from it as long as we need it.
310
+ let current_node = match self.files.get(absolute_path) {
311
+ Some(node) => {
312
+ info!("Found node for absolute_path: {}", absolute_path);
313
+ node.node.clone()
314
+ }
315
+ None => {
316
+ info!("File not found for absolute_path: {}", absolute_path);
317
+ return Err(anyhow::anyhow!("File not found: {}", absolute_path));
318
+ }
319
+ };
263
320
 
264
- // algorithm:
265
- // 1.
321
+ let mut stack = Vec::new();
322
+ stack.push(current_node);
323
+
324
+ while let Some(node) = stack.pop() {
325
+ let parent = node.read().await.parent.clone();
326
+ if let Some(parent) = parent {
327
+ info!("Adding parent hash to files vector");
328
+ {
329
+ let parent_node = parent.read().await;
330
+ match &parent_node.node_type {
331
+ NodeType::File(file_name) => {
332
+ files.push(file_name.clone());
333
+ }
334
+ NodeType::Branch((branch_name, _)) => {
335
+ files.push(branch_name.clone());
336
+ }
337
+ _ => {
338
+ continue;
339
+ }
340
+ }
341
+ }
266
342
 
267
- Err(anyhow::anyhow!("Could not find file to embed!"))
343
+ stack.push(parent);
344
+ }
345
+ }
346
+ info!("Returning files vector with {} elements", files.len());
347
+ Ok(files)
268
348
  }
269
349
 
270
350
  /// creates a new node and attaches it to the current tree.
@@ -302,12 +382,18 @@ impl MerkleTree {
302
382
  // 1. the path is empty. this means that the ancestor is the root.
303
383
  // 2. the path is non-empty. that means there exist a non-empty element btwn till the root.
304
384
 
385
+ let absolute_root_path = self.root_path.clone();
305
386
  let new_node = match path.len() {
306
387
  0 => {
307
388
  // this means that the ancestor is the root.
308
389
  // we need to create a new node and attach it to the ancestor.
309
- let new_node =
310
- MerkleNode::new(file_path.clone(), Some(ancestor.clone())).await;
390
+ let new_node = MerkleNode::new(
391
+ file_path.clone(),
392
+ Some(ancestor.clone()),
393
+ &self.git_ignored_files,
394
+ &absolute_root_path.as_str(),
395
+ )
396
+ .await;
311
397
  ancestor.write().await.attach_child(new_node.clone()).await;
312
398
  new_node
313
399
  }
@@ -318,9 +404,13 @@ impl MerkleTree {
318
404
  // UNSURE: not sure this is the correct thing to do but it is the fastest.
319
405
  // get the last thing that is not in the tree.
320
406
  let first_child_path = path.last().unwrap();
321
- let first_child =
322
- MerkleNode::new(first_child_path.clone(), Some(ancestor.clone()))
323
- .await;
407
+ let first_child = MerkleNode::new(
408
+ first_child_path.clone(),
409
+ Some(ancestor.clone()),
410
+ &self.git_ignored_files,
411
+ &absolute_root_path.as_str(),
412
+ )
413
+ .await;
324
414
 
325
415
  // TODO(sualeh): we should do an assertion check that the entire vec is contained here.
326
416
 
@@ -597,18 +687,62 @@ use std::future::Future;
597
687
  use std::pin::Pin;
598
688
 
599
689
  type PinnedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
690
+ type IgnoredFiles = HashSet<String>;
600
691
 
601
692
  impl MerkleNode {
602
693
  /// please be careful using this.
603
694
  async fn __new_unchecked(
604
695
  file_or_directory: String,
605
696
  parent: ParentPtr,
697
+ ignored_files: &IgnoredFiles,
698
+ absolute_root_path: &str,
606
699
  ) -> MerkleNodePtr {
607
- MerkleNode::construct_node(Path::new(&file_or_directory), parent).await
700
+ // check if the root is a git directory.
701
+ let is_git_repo =
702
+ match git_utils::is_git_directory(absolute_root_path).await {
703
+ Ok(is_git_repo) => is_git_repo,
704
+ Err(e) => false,
705
+ };
706
+ let bypass_git = !is_git_repo;
707
+
708
+ MerkleNode::construct_node(
709
+ Path::new(&file_or_directory),
710
+ parent,
711
+ ignored_files,
712
+ absolute_root_path,
713
+ bypass_git,
714
+ )
715
+ .await
608
716
  }
609
717
 
610
- async fn new(file_or_directory: PathBuf, parent: ParentPtr) -> MerkleNodePtr {
611
- MerkleNode::construct_node(Path::new(&file_or_directory), parent).await
718
+ async fn new(
719
+ absolute_file_or_directory: PathBuf,
720
+ parent: ParentPtr,
721
+ ignored_files: &IgnoredFiles,
722
+ absolute_root_path: &str,
723
+ ) -> MerkleNodePtr {
724
+ // check if the root is a git directory.
725
+ let is_git_repo =
726
+ match git_utils::is_git_directory(absolute_root_path).await {
727
+ Ok(is_git_repo) => is_git_repo,
728
+ Err(_e) => false,
729
+ };
730
+ let bypass_git = !is_git_repo;
731
+
732
+ info!(
733
+ "constructing node for absolute_file_or_directory: {:?}",
734
+ absolute_file_or_directory
735
+ );
736
+ info!("bypass_git: {}, is_git_repo: {}", bypass_git, is_git_repo);
737
+
738
+ MerkleNode::construct_node(
739
+ Path::new(&absolute_file_or_directory),
740
+ parent,
741
+ ignored_files,
742
+ absolute_root_path,
743
+ bypass_git,
744
+ )
745
+ .await
612
746
  }
613
747
 
614
748
  /// NOT added to the tree by default.
@@ -619,38 +753,68 @@ impl MerkleNode {
619
753
  // let file_hash = self.files.get_mut(&file_path).unwrap();
620
754
 
621
755
  fn construct_node<'a>(
622
- file_or_directory: &'a Path,
756
+ absolute_file_or_directory: &'a Path,
623
757
  parent: ParentPtr,
758
+ ignored_files: &'a IgnoredFiles,
759
+ absolute_root_path: &'a str,
760
+ bypass_git: bool,
624
761
  ) -> PinnedFuture<'a, MerkleNodePtr> {
625
762
  Box::pin(async move {
626
763
  // check if it is a file
627
- let path_str = file_or_directory.to_str().unwrap().to_string();
628
- if file_or_directory.is_file() {
764
+ let path_str = absolute_file_or_directory.to_str().unwrap().to_string();
765
+ if absolute_file_or_directory.is_file() {
629
766
  return Arc::new(RwLock::new(
630
767
  MerkleNode::construct_file_node_or_error_node(
631
- file_or_directory,
768
+ absolute_file_or_directory,
632
769
  parent,
770
+ ignored_files,
633
771
  )
634
772
  .await,
635
773
  ));
636
774
  }
637
775
 
638
776
  // check if the directory fails the bad dir test.
639
- let is_bad_dir = file_utils::is_in_bad_dir(file_or_directory);
777
+ let is_bad_dir = file_utils::is_in_bad_dir(absolute_file_or_directory);
640
778
  if is_bad_dir.is_err() || is_bad_dir.unwrap_or(false) {
641
779
  // println!("skipping directory: {}", path_str);
642
780
  return Arc::new(RwLock::new(MerkleNode::empty_node(
643
- Some(file_or_directory),
781
+ Some(absolute_file_or_directory),
644
782
  Some("Directory is in bad dir!".to_string()),
645
783
  )));
646
784
  }
647
785
 
648
- let entries = fs::read_dir(file_or_directory);
786
+ // check if the directory is git ignored
787
+ let is_git_ignored =
788
+ match git_utils::is_git_ignored(absolute_root_path, path_str.as_str())
789
+ .await
790
+ {
791
+ Ok(is_git_ignored) => is_git_ignored,
792
+ Err(e) => {
793
+ return Arc::new(RwLock::new(MerkleNode::empty_node(
794
+ Some(absolute_file_or_directory),
795
+ Some(e.to_string()),
796
+ )));
797
+ }
798
+ };
799
+
800
+ if is_git_ignored && !bypass_git {
801
+ // println!("skipping directory: {}", path_str);
802
+ tracing::info!(
803
+ "skipping directory because its git ignored: {}",
804
+ path_str
805
+ );
806
+ return Arc::new(RwLock::new(MerkleNode::empty_node(
807
+ Some(absolute_file_or_directory),
808
+ Some("Directory is git ignored!".to_string()),
809
+ )));
810
+ }
811
+
812
+ let entries = fs::read_dir(absolute_file_or_directory);
649
813
  match entries {
650
814
  Ok(_) => (),
651
815
  Err(e) => {
652
816
  return Arc::new(RwLock::new(MerkleNode::empty_node(
653
- Some(file_or_directory),
817
+ Some(absolute_file_or_directory),
654
818
  Some(e.to_string()),
655
819
  )));
656
820
  }
@@ -670,13 +834,19 @@ impl MerkleNode {
670
834
  match entry {
671
835
  Ok(entry) => {
672
836
  children.push(
673
- MerkleNode::construct_node(&entry.path(), Some(node.clone()))
674
- .await,
837
+ MerkleNode::construct_node(
838
+ &entry.path(),
839
+ Some(node.clone()),
840
+ ignored_files,
841
+ absolute_root_path,
842
+ bypass_git,
843
+ )
844
+ .await,
675
845
  );
676
846
  }
677
847
  Err(e) => {
678
848
  children.push(Arc::new(RwLock::new(MerkleNode::empty_node(
679
- Some(file_or_directory),
849
+ Some(absolute_file_or_directory),
680
850
  Some(e.to_string()),
681
851
  ))));
682
852
  }
@@ -696,23 +866,33 @@ impl MerkleNode {
696
866
  }
697
867
 
698
868
  async fn construct_file_node(
699
- file_path: &Path,
869
+ absolute_file_path: &Path,
700
870
  parent: ParentPtr,
871
+ ignored_files: &IgnoredFiles,
701
872
  ) -> Result<MerkleNode, String> {
702
- let file_str = file_path
873
+ let file_str = absolute_file_path
703
874
  .to_str()
704
875
  .ok_or("Could not convert file path to string!")?
705
876
  .to_string();
706
877
  // first see if it passes the
707
- match file_utils::is_good_file(file_path) {
878
+ match file_utils::is_good_file(absolute_file_path) {
708
879
  Ok(_) => {}
709
880
  Err(e) => {
710
881
  return Err(format!("File failed runtime checks! {}", e.to_string()));
711
882
  }
712
883
  }
713
884
 
885
+ // check if the file is in the git ignore buffer.
886
+ // this is a bug right because we are not checking absoluteness here.
887
+ match ignored_files.contains(&file_str) {
888
+ true => {
889
+ return Err(format!("File is in git ignore buffer!"));
890
+ }
891
+ false => {}
892
+ }
893
+
714
894
  // read the file_content to a buffer
715
- let file_content = match tokio::fs::read(file_path).await {
895
+ let file_content = match tokio::fs::read(absolute_file_path).await {
716
896
  Ok(content) => content,
717
897
  Err(e) => {
718
898
  return Err(format!("Could not read file! {}", e.to_string()));
@@ -720,7 +900,11 @@ impl MerkleNode {
720
900
  };
721
901
 
722
902
  // check if the file passes runtime checks.
723
- match file_utils::is_good_file_runtime_check(file_path, &file_content).await
903
+ match file_utils::is_good_file_runtime_check(
904
+ absolute_file_path,
905
+ &file_content,
906
+ )
907
+ .await
724
908
  {
725
909
  Ok(_) => {}
726
910
  Err(e) => {
@@ -751,15 +935,22 @@ impl MerkleNode {
751
935
  }
752
936
 
753
937
  async fn construct_file_node_or_error_node(
754
- file_path: &Path,
938
+ absolute_file_path: &Path,
755
939
  parent: ParentPtr,
940
+ ignored_files: &IgnoredFiles,
756
941
  ) -> MerkleNode {
757
- let node = match MerkleNode::construct_file_node(file_path, parent).await {
942
+ let node = match MerkleNode::construct_file_node(
943
+ absolute_file_path,
944
+ parent,
945
+ ignored_files,
946
+ )
947
+ .await
948
+ {
758
949
  Ok(node) => node,
759
950
  Err(e) => {
760
951
  // println!("constructing error node. error: {}", e);
761
952
  // println!("file_path: {:?}", file_path);
762
- MerkleNode::empty_node(Some(file_path), Some(e))
953
+ MerkleNode::empty_node(Some(absolute_file_path), Some(e))
763
954
  }
764
955
  };
765
956
 
package/src/test.rs ADDED
@@ -0,0 +1,5 @@
1
+ #[cfg(test)]
2
+ mod tests {
3
+ use super::super::*;
4
+ use std::path::PathBuf;
5
+ }