1use std::{
2 fmt::Display,
3 fs::File,
4 io::{ErrorKind, IoSlice, Write},
5 path::PathBuf,
6};
7
8use decrypt_cookies::{chromium::ChromiumCookie, prelude::cookies::CookiesInfo};
9use snafu::ResultExt;
10use tokio::task::{self, JoinHandle};
11
12use crate::error;
13
14pub(crate) fn write_all_vectored(
17 file: &mut File,
18 mut bufs: &mut [IoSlice<'_>],
19) -> std::io::Result<()> {
20 IoSlice::advance_slices(&mut bufs, 0);
21 while !bufs.is_empty() {
22 match file.write_vectored(bufs) {
23 Ok(0) => {
24 return Err(std::io::Error::new(
25 std::io::ErrorKind::WriteZero,
26 "failed to write whole buffer",
27 ));
28 },
29 Ok(n) => IoSlice::advance_slices(&mut bufs, n),
30 Err(ref e) if matches!(e.kind(), ErrorKind::Interrupted) => {},
31 Err(e) => return Err(e),
32 }
33 }
34 Ok(())
35}
36
37pub(crate) fn write_cookies<S>(
38 out_file: PathBuf,
39 cookies: Vec<impl CookiesInfo + Send + 'static>,
40 sep: S,
41) -> JoinHandle<Result<(), error::Error>>
42where
43 S: Display + Send + Clone + 'static,
44{
45 task::spawn_blocking(move || {
46 let mut file = File::options()
47 .write(true)
48 .create(true)
49 .truncate(true)
50 .open(&out_file)
51 .context(error::IoSnafu { path: out_file.clone() })?;
52
53 let header = <ChromiumCookie as CookiesInfo>::csv_header(sep.clone());
54
55 let mut slices = Vec::with_capacity(2 + cookies.len() * 2);
56 slices.push(IoSlice::new(header.as_bytes()));
57 slices.push(IoSlice::new(b"\n"));
58
59 let csvs: Vec<_> = cookies
60 .into_iter()
61 .map(|v| v.to_csv(sep.clone()))
62 .collect();
63
64 for csv in &csvs {
65 slices.push(IoSlice::new(csv.as_bytes()));
66 slices.push(IoSlice::new(b"\n"));
67 }
68
69 write_all_vectored(&mut file, &mut slices)
70 .context(error::IoSnafu { path: out_file.clone() })?;
71
72 Ok::<(), error::Error>(())
73 })
74}