1use ssec_core::Encrypt;
2use futures_util::StreamExt;
3use tokio::io::AsyncWriteExt;
4use zeroize::Zeroizing;
5use rand::rngs::OsRng;
6use indicatif::{ProgressBar, ProgressStyle};
7use crate::cli::EncArgs;
8use crate::BAR_STYLE;
9
10const SPINNER_STYLE: &str = "{spinner} deriving encryption key";
11
12pub async fn enc(args: EncArgs) -> Result<(), ()> {
13 let password = tokio::task::spawn_blocking(move || {
14 rpassword::prompt_password("password: ")
15 .map(Zeroizing::new)
16 }).await.unwrap().map_err(|e| {
17 eprintln!("failed to read password interactively: {e}");
18 })?;
19
20 let f_in = tokio::fs::File::open(&args.in_file).await.map_err(|e| {
21 eprintln!("failed to open file {:?}: {e}", args.in_file);
22 })?;
23 let f_in_metadata = f_in.metadata().await.map_err(|e| {
24 eprintln!("failed to get metadata of input file: {e}");
25 })?;
26
27 if !f_in_metadata.is_file() {
28 eprintln!("ERROR: input file {:?} is not, in fact, a file", args.in_file);
29 return Err(());
30 }
31
32 let f_in_len = f_in_metadata.len();
33 if f_in_len == 0 {
34 eprintln!("input file {:?} is empty", args.in_file);
35 return Ok(());
36 }
37
38 let progress = ProgressBar::new(f_in_len);
39 progress.set_style(ProgressStyle::with_template(BAR_STYLE).unwrap());
40 let f_in = progress.wrap_async_read(f_in);
41
42 let s = tokio_util::io::ReaderStream::new(f_in);
43 let mut enc = tokio::task::spawn_blocking(move || {
44 let spinner = ProgressBar::new_spinner();
45 spinner.set_style(ProgressStyle::with_template(SPINNER_STYLE).unwrap());
46 spinner.enable_steady_tick(std::time::Duration::from_millis(100));
47 Encrypt::new_uncompressed(s, password.as_bytes(), &mut OsRng, f_in_len)
48 }).await.unwrap().unwrap();
49
50 let mut f_out = match args.out_file {
51 Some(out_file) => {
52 tokio::fs::OpenOptions::new()
53 .create(true)
54 .write(true)
55 .truncate(true)
56 .open(&out_file).await.map_err(|e| {
57 eprintln!("failed to open specified outout file {out_file:?}: {e}");
58 }).map(tokio::io::BufWriter::new)?
59 },
60 None => {
61 let out_name = format!("{}.ssec", args.in_file.display());
62 tokio::fs::File::create_new(&out_name).await.map_err(|e| {
63 eprintln!("failed to create new output file {out_name:?}: {e}");
64 }).map(tokio::io::BufWriter::new)?
65 }
66 };
67
68 progress.reset();
69
70 while let Some(bytes) = enc.next().await {
71 f_out.write_all(&bytes.unwrap()).await.unwrap();
72 }
73
74 f_out.shutdown().await.unwrap();
75
76 Ok(())
77}