diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 74 |
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 | ||
| 13 | use clap::{Args, Parser, Subcommand}; | 13 | use clap::{Args, Parser, Subcommand, ValueEnum}; |
| 14 | use sha2::Digest; | 14 | use sha2::Digest; |
| 15 | use slotmap::SlotMap; | 15 | use 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)] |
| 81 | pub enum ImportMode { | 81 | pub 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)] | ||
| 1191 | mod test { | ||
| 1192 | use super::*; | ||
| 1193 | |||
| 1194 | #[test] | ||
| 1195 | fn parse_drive_path() { | ||
| 1196 | let p = "/".parse::<DrivePath>().unwrap(); | ||
| 1197 | } | ||
| 1198 | } | ||
