fencryption_lib/commands/
encrypt_file.rs1use std::{fs, iter::zip, path::PathBuf, sync::mpsc::channel, time};
4
5use threadpool::ThreadPool;
6use uuid::Uuid;
7
8use crate::{
9 commands::{logic, Command, Error, ErrorBuilder, Result},
10 crypto::Crypto,
11 walk_dir::walk_dir,
12};
13
14pub fn execute(
16 key: &String,
17 paths: &Vec<PathBuf>,
18 output_path: &Option<PathBuf>,
19 overwrite: &bool,
20 delete_original: &bool,
21) -> Result<(
22 u32,
23 Vec<(PathBuf, Error)>,
24 Vec<(PathBuf, Error)>,
25 time::Duration,
26)> {
27 let timer = time::SystemTime::now();
28
29 let output_paths = logic::get_output_paths(&paths, &output_path, Command::EncryptFile);
30
31 logic::checks(&paths, &output_path)?;
32 logic::overwrite(&output_paths, *overwrite)?;
33
34 let mut success: u32 = 0;
35 let mut failures: Vec<(PathBuf, Error)> = Vec::new();
36 let mut skips: Vec<(PathBuf, Error)> = Vec::new();
37
38 let crypto = Crypto::new(key).map_err(|e| {
39 ErrorBuilder::new()
40 .message("Failed to initialize encryption utils")
41 .error(e)
42 .build()
43 })?;
44
45 for (main_input_path, main_output_path) in zip(paths.to_owned(), output_paths) {
46 match main_input_path.to_owned() {
47 dir_path if dir_path.is_dir() => {
48 fs::create_dir(&main_output_path).map_err(|e| {
49 ErrorBuilder::new()
50 .message("Failed to create output directory")
51 .error(e)
52 .build()
53 })?;
54
55 let walk_dir = walk_dir(&dir_path).map_err(|e| {
56 ErrorBuilder::new()
57 .message("Failed to read directory")
58 .error(e)
59 .build()
60 })?;
61
62 let threadpool = ThreadPool::new(8);
63 let (tx, rx) = channel();
64 let mut tries_n = 0;
65
66 for dir_entry in walk_dir {
67 let entry = dir_entry.map_err(|e| {
68 ErrorBuilder::new()
69 .message("Failed to read directory entry")
70 .error(e)
71 .build()
72 })?;
73 let input_path = entry.path();
74 let relative_entry_path = input_path
75 .strip_prefix(&dir_path)
76 .map_err(|e| {
77 ErrorBuilder::new()
78 .message("Failed to get relative entry path")
79 .error(e)
80 .build()
81 })?
82 .to_owned();
83 let output_path = main_output_path.join(Uuid::new_v4().to_string());
84
85 match input_path {
86 input_path if input_path.is_file() => {
87 tries_n += 1;
88
89 let crypto = crypto.clone();
90 let tx = tx.clone();
91 let output_path = output_path.clone();
92
93 threadpool.execute(move || {
94 let result = logic::encrypt_file(
95 crypto,
96 &input_path,
97 &output_path,
98 Some(relative_entry_path.to_owned()),
99 );
100 tx.send((input_path.to_owned(), result)).unwrap();
101 });
102 }
103 input_path => {
104 if !input_path.is_dir() {
105 skips.push((
106 input_path,
107 ErrorBuilder::new().message("Unknown entry type").build(),
108 ));
109 }
110 }
111 }
112 }
113
114 threadpool.join();
115 rx.iter()
116 .take(tries_n)
117 .for_each(|(path, result)| match result {
118 Ok(_) => success += 1,
119 Err(e) => failures.push((path, e)),
120 })
121 }
122 path if path.is_file() => {
123 match logic::encrypt_file(crypto.to_owned(), &path, &main_output_path, None) {
124 Ok(_) => success += 1,
125 Err(e) => failures.push((path, e)),
126 };
127 }
128 path => skips.push((
129 path,
130 ErrorBuilder::new().message("Unknown entry type").build(),
131 )),
132 }
133
134 logic::delete_original(&main_input_path, *delete_original)?;
135 }
136
137 Ok((
138 success,
139 failures,
140 skips,
141 timer.elapsed().map_err(|e| {
142 ErrorBuilder::new()
143 .message("Failed to get elapsed time")
144 .error(e)
145 .build()
146 })?,
147 ))
148}