summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-08-11 11:51:39 +0100
committerdiogo464 <[email protected]>2025-08-11 11:51:39 +0100
commit4af66f418b6837b6441b4e8eaf2d8ede585238b9 (patch)
tree34a4e913a2848515166b2ac0489794419a33bfcc /src/main.rs
parent0d3488a3811c8d58bd570af64cc29840df9ba439 (diff)
snapshot
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs74
1 files changed, 59 insertions, 15 deletions
diff --git a/src/main.rs b/src/main.rs
index 3a8e5f9..00091e6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,7 +10,7 @@ use std::{
10 time::SystemTime, 10 time::SystemTime,
11}; 11};
12 12
13use clap::{Args, Parser, Subcommand}; 13use clap::{Args, Parser, Subcommand, ValueEnum};
14use sha2::Digest; 14use sha2::Digest;
15use slotmap::SlotMap; 15use slotmap::SlotMap;
16 16
@@ -77,7 +77,7 @@ pub fn blob_path(store: &BlobStore, blob_id: &BlobId) -> PathBuf {
77 path 77 path
78} 78}
79 79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
81pub enum ImportMode { 81pub enum ImportMode {
82 Move, 82 Move,
83 Copy, 83 Copy,
@@ -103,7 +103,13 @@ pub fn blob_import_file(
103 std::fs::rename(file_path, blob_path)?; 103 std::fs::rename(file_path, blob_path)?;
104 } 104 }
105 ImportMode::Copy => { 105 ImportMode::Copy => {
106 todo!() 106 let blob_tmp_path = {
107 let mut p = blob_path.clone();
108 p.set_file_name(format!("{blob_id}.tmp"));
109 p
110 };
111 std::fs::copy(file_path, &blob_tmp_path)?;
112 std::fs::rename(&blob_tmp_path, blob_path)?;
107 } 113 }
108 ImportMode::HardLink => match std::fs::hard_link(file_path, blob_path) { 114 ImportMode::HardLink => match std::fs::hard_link(file_path, blob_path) {
109 Ok(()) => {} 115 Ok(()) => {}
@@ -315,6 +321,12 @@ impl DrivePath {
315 Self(components) 321 Self(components)
316 } 322 }
317 323
324 pub fn join(&self, path: DrivePath) -> DrivePath {
325 let mut components = self.0.clone();
326 components.extend(path.0);
327 Self(components)
328 }
329
318 pub fn components(&self) -> &[DrivePathComponent] { 330 pub fn components(&self) -> &[DrivePathComponent] {
319 self.0.as_slice() 331 self.0.as_slice()
320 } 332 }
@@ -349,7 +361,11 @@ impl FromStr for DrivePath {
349 361
350 fn from_str(s: &str) -> Result<Self, Self::Err> { 362 fn from_str(s: &str) -> Result<Self, Self::Err> {
351 let mut components = Vec::default(); 363 let mut components = Vec::default();
352 for unchecked_component in s.trim().trim_matches('/').split('/') { 364 for unchecked_component in s.trim_matches('/').split('/') {
365 if unchecked_component.is_empty() {
366 continue;
367 }
368
353 match unchecked_component.parse() { 369 match unchecked_component.parse() {
354 Ok(component) => components.push(component), 370 Ok(component) => components.push(component),
355 Err(err) => { 371 Err(err) => {
@@ -773,6 +789,9 @@ struct LsArgs {
773 #[clap(short, long)] 789 #[clap(short, long)]
774 recursive: bool, 790 recursive: bool,
775 791
792 #[clap(long)]
793 relative: bool,
794
776 path: Option<DrivePath>, 795 path: Option<DrivePath>,
777} 796}
778 797
@@ -782,11 +801,17 @@ struct ImportArgs {
782 common: CliCommon, 801 common: CliCommon,
783 802
784 #[clap(long)] 803 #[clap(long)]
804 mode: ImportMode,
805
806 #[clap(long)]
785 timestamp: Option<u64>, 807 timestamp: Option<u64>,
786 808
787 #[clap(long)] 809 #[clap(long)]
788 email: String, 810 email: String,
789 811
812 #[clap(long, default_value = "/")]
813 destination: DrivePath,
814
790 path: PathBuf, 815 path: PathBuf,
791} 816}
792 817
@@ -921,8 +946,8 @@ fn cmd_ls(args: LsArgs) {
921 let mut fs = Fs::default(); 946 let mut fs = Fs::default();
922 let ops = common_read_log_file(&args.common); 947 let ops = common_read_log_file(&args.common);
923 ops.iter().for_each(|op| apply(&mut fs, op).unwrap()); 948 ops.iter().for_each(|op| apply(&mut fs, op).unwrap());
924 let node_id = match args.path { 949 let node_id = match &args.path {
925 Some(path) => find_node(&fs, &path), 950 Some(path) => find_node(&fs, path),
926 None => Some(fs.root), 951 None => Some(fs.root),
927 }; 952 };
928 if let Some(node_id) = node_id { 953 if let Some(node_id) = node_id {
@@ -930,7 +955,11 @@ fn cmd_ls(args: LsArgs) {
930 &mut writer, 955 &mut writer,
931 &fs, 956 &fs,
932 node_id, 957 node_id,
933 Default::default(), 958 if args.relative {
959 Default::default()
960 } else {
961 args.path.unwrap_or_default()
962 },
934 args.recursive, 963 args.recursive,
935 true, 964 true,
936 ); 965 );
@@ -958,12 +987,14 @@ fn write_node(
958 .unwrap(); 987 .unwrap();
959 } 988 }
960 FsNodeKind::Directory => { 989 FsNodeKind::Directory => {
961 writeln!( 990 if !recurse {
962 writer, 991 writeln!(
963 "{}\tdir\t{}\t-\t-\t{}", 992 writer,
964 path, node.lastmod, node.author 993 "{}\tdir\t{}\t-\t-\t{}",
965 ) 994 path, node.lastmod, node.author
966 .unwrap(); 995 )
996 .unwrap();
997 }
967 if recursive || recurse { 998 if recursive || recurse {
968 for (child_comp, child_id) in node.children.iter() { 999 for (child_comp, child_id) in node.children.iter() {
969 let child_path = path.push(child_comp.clone()); 1000 let child_path = path.push(child_comp.clone());
@@ -1007,6 +1038,7 @@ fn cmd_import(args: ImportArgs) {
1007 let store = BlobStore::new("blobs"); 1038 let store = BlobStore::new("blobs");
1008 let timestamp = args.timestamp.unwrap_or_else(get_timestamp); 1039 let timestamp = args.timestamp.unwrap_or_else(get_timestamp);
1009 let root = args.path.canonicalize().unwrap(); 1040 let root = args.path.canonicalize().unwrap();
1041 let import_mode = args.mode;
1010 1042
1011 let files = Queue::from(collect_all_file_paths(&root)); 1043 let files = Queue::from(collect_all_file_paths(&root));
1012 let num_threads = std::thread::available_parallelism().unwrap().get(); 1044 let num_threads = std::thread::available_parallelism().unwrap().get();
@@ -1016,13 +1048,15 @@ fn cmd_import(args: ImportArgs) {
1016 let email = args.email.clone(); 1048 let email = args.email.clone();
1017 let files = files.clone(); 1049 let files = files.clone();
1018 let store = store.clone(); 1050 let store = store.clone();
1051 let destination = args.destination.clone();
1019 let handle = std::thread::spawn(move || { 1052 let handle = std::thread::spawn(move || {
1020 let mut ops = Vec::default(); 1053 let mut ops = Vec::default();
1021 while let Some(file) = files.pop() { 1054 while let Some(file) = files.pop() {
1022 let rel = file.strip_prefix(&root).unwrap(); 1055 let rel = file.strip_prefix(&root).unwrap();
1023 let drive_path = rel.to_str().unwrap().parse::<DrivePath>().unwrap(); 1056 let drive_path =
1057 destination.join(rel.to_str().unwrap().parse::<DrivePath>().unwrap());
1024 let blob_id = blob_hash_file(&file).unwrap(); 1058 let blob_id = blob_hash_file(&file).unwrap();
1025 blob_import_file(&store, ImportMode::HardLink, &blob_id, &file).unwrap(); 1059 blob_import_file(&store, import_mode, &blob_id, &file).unwrap();
1026 let blob_size = blob_size(&store, &blob_id).unwrap(); 1060 let blob_size = blob_size(&store, &blob_id).unwrap();
1027 let op = Operation { 1061 let op = Operation {
1028 header: OperationHeader { 1062 header: OperationHeader {
@@ -1152,3 +1186,13 @@ fn get_next_revision(ops: &[Operation]) -> u64 {
1152 None => 0, 1186 None => 0,
1153 } 1187 }
1154} 1188}
1189
1190#[cfg(test)]
1191mod test {
1192 use super::*;
1193
1194 #[test]
1195 fn parse_drive_path() {
1196 let p = "/".parse::<DrivePath>().unwrap();
1197 }
1198}