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