Skip to main content

ssec_cli/
enc.rs

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