fencryption_lib/commands/
encrypt_file.rs

1//! Encrypt file.
2
3use 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
14/// Encrypts the files associated to the given paths.
15pub 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}