diff --git a/index.d.ts b/index.d.ts index a084fe0..96a97a3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,6 +3,7 @@ /* auto-generated by NAPI-RS */ +export declare function hasBackendForPath(path: string): boolean export declare function callAltThreadFunc(callback: (...args: any[]) => any): void export declare function generateManifest(dir: string, progress: (...args: any[]) => any, log: (...args: any[]) => any, callback: (...args: any[]) => any): void export declare function generateRootCa(): Array diff --git a/index.js b/index.js index 862a87f..28a34cb 100644 --- a/index.js +++ b/index.js @@ -310,8 +310,9 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { callAltThreadFunc, generateManifest, generateRootCa, generateClientCertificate, verifyClientCertificate, signNonce, verifyNonce } = nativeBinding +const { hasBackendForPath, callAltThreadFunc, generateManifest, generateRootCa, generateClientCertificate, verifyClientCertificate, signNonce, verifyNonce } = nativeBinding +module.exports.hasBackendForPath = hasBackendForPath module.exports.callAltThreadFunc = callAltThreadFunc module.exports.generateManifest = generateManifest module.exports.generateRootCa = generateRootCa diff --git a/package.json b/package.json index ed00fc3..6f70089 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@drop-oss/droplet", - "version": "0.7.2", + "version": "1.0.0", "main": "index.js", "types": "index.d.ts", "napi": { diff --git a/src/file_utils.rs b/src/file_utils.rs index 8fc1dab..6eea987 100644 --- a/src/file_utils.rs +++ b/src/file_utils.rs @@ -1,5 +1,9 @@ +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; use std::{ - fs::{self, metadata}, + fs::{self, metadata, File}, + io::{BufReader, Read}, + os::unix::fs::MetadataExt, path::{Path, PathBuf}, }; @@ -17,8 +21,96 @@ fn _list_files(vec: &mut Vec, path: &Path) { } } -pub fn list_files(path: &Path) -> Vec { - let mut vec = Vec::new(); - _list_files(&mut vec, path); - vec +pub struct VersionFile { + pub relative_filename: String, + pub size: u64, + pub permission: u32, +} + +pub trait VersionBackend: 'static { + fn list_files(&self, path: &Path) -> Vec; + fn reader(&self, file: &VersionFile) -> BufReader; +} + +pub struct PathVersionBackend { + pub base_dir: PathBuf, +} +impl VersionBackend for PathVersionBackend { + fn list_files(&self, path: &Path) -> Vec { + let mut vec = Vec::new(); + _list_files(&mut vec, path); + + let mut results = Vec::new(); + + for pathbuf in vec.iter() { + let file = File::open(pathbuf.clone()).unwrap(); + let relative = pathbuf.strip_prefix(path).unwrap(); + let metadata = file.try_clone().unwrap().metadata().unwrap(); + let permission_object = metadata.permissions(); + let permissions = { + let perm: u32; + #[cfg(target_family = "unix")] + { + perm = permission_object.mode(); + } + #[cfg(not(target_family = "unix"))] + { + perm = 0 + } + perm + }; + let size = metadata.size(); + + results.push(VersionFile { + relative_filename: relative.to_string_lossy().to_string(), + size: size, + permission: permissions, + }); + } + + results + } + + fn reader(&self, file: &VersionFile) -> BufReader { + let file = File::open(self.base_dir.join(file.relative_filename.clone())).unwrap(); + let reader = BufReader::with_capacity(4096, file); + return reader; + } +} + +// Todo implementation for archives +// Split into a separate impl for each type of archive +pub struct ArchiveVersionBackend {} +impl VersionBackend for ArchiveVersionBackend { + fn list_files(&self, path: &Path) -> Vec { + todo!() + } + + fn reader(&self, file: &VersionFile) -> BufReader { + todo!() + } +} + +pub fn create_backend_for_path(path: &Path) -> Option> { + let is_directory = path.is_dir(); + if is_directory { + return Some(Box::new(PathVersionBackend { + base_dir: path.to_path_buf(), + })); + }; + + /* + Insert checks for whatever backend you like + */ + + None +} + +#[napi] +pub fn has_backend_for_path(path: String) -> bool { + let path = Path::new(&path); + + let has_backend = create_backend_for_path(path).is_some(); + + has_backend } diff --git a/src/manifest.rs b/src/manifest.rs index 4baa838..f30a61d 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -16,7 +16,7 @@ use napi::{ use serde_json::json; use uuid::Uuid; -use crate::file_utils::list_files; +use crate::file_utils::create_backend_for_path; const CHUNK_SIZE: usize = 1024 * 1024 * 64; @@ -64,7 +64,8 @@ pub fn generate_manifest( thread::spawn(move || { let base_dir = Path::new(&dir); - let files = list_files(base_dir); + let backend = create_backend_for_path(base_dir).unwrap(); + let files = backend.list_files(base_dir); // Filepath to chunk data let mut chunks: HashMap = HashMap::new(); @@ -72,27 +73,11 @@ pub fn generate_manifest( let total: i32 = files.len() as i32; let mut i: i32 = 0; - for file_path in files { - let file = File::open(file_path.clone()).unwrap(); - let relative = file_path.strip_prefix(base_dir).unwrap(); - let permission_object = file.try_clone().unwrap().metadata().unwrap().permissions(); - let permissions = { - let perm: u32; - #[cfg(target_family = "unix")] - { - perm = permission_object.mode(); - } - #[cfg(not(target_family = "unix"))] - { - perm = 0 - } - perm - }; - - let mut reader = BufReader::with_capacity(CHUNK_SIZE, file); + for version_file in files { + let mut reader = backend.reader(&version_file); let mut chunk_data = ChunkData { - permissions, + permissions: version_file.permission, ids: Vec::new(), checksums: Vec::new(), lengths: Vec::new(), @@ -119,7 +104,7 @@ pub fn generate_manifest( let log_str = format!( "Processed chunk {} for {}", chunk_index, - relative.to_str().unwrap() + &version_file.relative_filename ); log_sfn.call(Ok(log_str), ThreadsafeFunctionCallMode::Blocking); @@ -127,7 +112,7 @@ pub fn generate_manifest( chunk_index += 1; } - chunks.insert(relative.to_str().unwrap().to_string(), chunk_data); + chunks.insert(version_file.relative_filename, chunk_data); i += 1; let progress = i * 100 / total;