fencryption_lib/commands/
decrypt_file.rs

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