1use std::{collections::HashSet, fmt::Display, path::PathBuf};
2
3use decrypt_cookies::{firefox::builder::FirefoxBuilderError, prelude::*};
4use snafu::ResultExt;
5use strum::IntoEnumIterator;
6use tokio::task;
7
8use crate::{
9 args::{FirefoxName, Format, Value},
10 error::{self, Result},
11 utils::{self},
12};
13
14#[derive(Clone, Copy)]
15#[derive(Debug)]
16#[derive(Default)]
17#[derive(PartialEq, Eq, PartialOrd, Ord)]
18pub struct FirefoxBased;
19
20impl FirefoxBased {
21 pub async fn multi_data<H>(
22 names: impl Iterator<Item = FirefoxName>,
23 output_dir: PathBuf,
24 sep: String,
25 host: H,
26 format: Format,
27 ) -> Result<()>
28 where
29 H: Into<Option<String>>,
30 {
31 let host = host.into();
32 for task in names.map(|name| {
33 let host = host.clone();
34 let output_dir = output_dir.clone();
35 let sep = sep.clone();
36 let values = HashSet::from_iter(Value::iter());
37
38 tokio::task::spawn(async move {
39 Self::write_data(
40 name, None, None, None, host, values, output_dir, sep, format,
41 )
42 .await
43 })
44 }) {
45 if let Err(e) = task
46 .await
47 .context(error::TokioTaskSnafu)?
48 {
49 match e {
50 error::Error::FirefoxBuilder {
51 source: FirefoxBuilderError::NotFoundBase { path, .. },
52 ..
53 } => {
54 tracing::info!(
55 r#"Not found {},
56The browser is not installed or started with `-P`/`-profile` arg."#,
57 path.display()
58 );
59 todo!()
60 },
61 e => tracing::error!("{e}"),
62 }
63 }
64 }
65
66 Ok(())
67 }
68
69 #[tracing::instrument(skip_all, fields(name), level = "info")]
70 #[expect(clippy::too_many_arguments, reason = "bin not lib")]
71 pub async fn write_data<B, P, PP, H, S>(
72 name: FirefoxName,
73 base: B,
74 profile: P,
75 profile_path: PP,
76 host: H,
77 values: HashSet<Value>,
78 mut output_dir: PathBuf,
79 sep: S,
80 format: Format,
81 ) -> Result<()>
82 where
83 B: Into<Option<PathBuf>>,
84 P: Into<Option<String>>,
85 PP: Into<Option<PathBuf>>,
86 H: Into<Option<String>>,
87 S: Display + Send + Clone + 'static,
88 {
89 let base: Option<PathBuf> = base.into();
90 let profile: Option<String> = profile.into();
91 let profile_path: Option<PathBuf> = profile_path.into();
92 let host: Option<String> = host.into();
93
94 macro_rules! firefoxes {
95 ($($browser:ident,) *) => {
96 match name {
97 $(
98 FirefoxName::$browser => {
99 let firefox = if let Some(pp) = profile_path {
100 FirefoxBuilder::<$browser>::with_profile_path(pp)
101 }
102 else {
103 let mut builder = FirefoxBuilder::<$browser>::new();
104 if let Some(base) = base {
105 builder.base(base);
106 }
107 if let Some(profile) = &profile {
108 builder.profile(profile);
109 }
110 builder
111 }
112 .build()
113 .await
114 .context(error::FirefoxBuilderSnafu)?;
115
116 let task = if values.contains(&Value::Cookie) {
117 let host = host.clone();
118 let firefox = firefox.clone();
119 let task = task::spawn(async move {
120 let cookies = if let Some(host) = host {
121 firefox.cookies_by_host(host).await
122 }
123 else {
124 firefox.cookies_all().await
125 }
126 .context(error::FirefoxSnafu)?;
127 Ok::<_, error::Error>(cookies)
128 });
129 Some(task)
130 }
131 else {
132 None
133 };
134
135 if values.contains(&Value::Login) {
136 }
138 (task, $browser::NAME)
139 },
140 )*
141 }
142 };
143 }
144
145 let (ff, name) = firefoxes![Firefox, Librewolf, Floorp, Zen,];
146
147 output_dir.push(name);
148 tokio::fs::create_dir_all(&output_dir)
149 .await
150 .with_context(|_| error::IoSnafu { path: output_dir.clone() })?;
151
152 if let Some(ff) = ff {
153 let cookies = ff
154 .await
155 .context(error::TokioTaskSnafu)??;
156 let out_file = output_dir.join(crate::COOKIES_FILE_CSV);
157 let sep = sep.clone();
158
159 utils::write_cookies(out_file, cookies, sep, format)
160 .await
161 .context(error::TokioTaskSnafu)??;
162 }
163
164 Ok(())
165 }
166}