1use std::{collections::HashSet, fmt::Display, path::PathBuf};
2
3use decrypt_cookies::prelude::*;
4use snafu::ResultExt;
5use strum::IntoEnumIterator;
6use tokio::task;
7
8use crate::{
9 args::{FirefoxName, 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 ) -> Result<()>
27 where
28 H: Into<Option<String>>,
29 {
30 let host = host.into();
31 for task in names.map(|name| {
32 let host = host.clone();
33 let output_dir = output_dir.clone();
34 let sep = sep.clone();
35 let values = HashSet::from_iter(Value::iter());
36
37 tokio::task::spawn(async move {
38 Self::write_data(name, None, None, None, host, values, output_dir, sep).await
39 })
40 }) {
41 task.await
42 .context(error::TokioTaskSnafu)??;
43 }
44
45 Ok(())
46 }
47
48 #[expect(clippy::too_many_arguments, reason = "bin not lib")]
49 pub async fn write_data<B, P, PP, H, S>(
50 name: FirefoxName,
51 base: B,
52 profile: P,
53 profile_path: PP,
54 host: H,
55 values: HashSet<Value>,
56 mut output_dir: PathBuf,
57 sep: S,
58 ) -> Result<()>
59 where
60 B: Into<Option<PathBuf>>,
61 P: Into<Option<String>>,
62 PP: Into<Option<PathBuf>>,
63 H: Into<Option<String>>,
64 S: Display + Send + Clone + 'static,
65 {
66 let base: Option<PathBuf> = base.into();
67 let profile: Option<String> = profile.into();
68 let profile_path: Option<PathBuf> = profile_path.into();
69 let host: Option<String> = host.into();
70
71 macro_rules! firefoxes {
72 ($($browser:ident,) *) => {
73 match name {
74 $(
75 FirefoxName::$browser => {
76 let firefox = if let Some(pp) = profile_path {
77 FirefoxBuilder::<$browser>::with_profile_path(pp)
78 }
79 else {
80 let mut builder = FirefoxBuilder::<$browser>::new();
81 if let Some(base) = base {
82 builder.base(base);
83 }
84 if let Some(profile) = &profile {
85 builder.profile(profile);
86 }
87 builder
88 }
89 .build()
90 .await
91 .context(error::FirefoxBuilderSnafu)?;
92
93 let task = if values.contains(&Value::Cookie) {
94 let host = host.clone();
95 let firefox = firefox.clone();
96 let task = task::spawn(async move {
97 let cookies = if let Some(host) = host {
98 firefox.cookies_by_host(host).await
99 }
100 else {
101 firefox.cookies_all().await
102 }
103 .context(error::FirefoxSnafu)?;
104 Ok::<_, error::Error>(cookies)
105 });
106 Some(task)
107 }
108 else {
109 None
110 };
111
112 if values.contains(&Value::Login) {
113 }
115 (task, $browser::NAME)
116 },
117 )*
118 }
119 };
120 }
121
122 let (ff, name) = firefoxes![Firefox, Librewolf, Floorp,];
123
124 output_dir.push(name);
125 tokio::fs::create_dir_all(&output_dir)
126 .await
127 .context(error::IoSnafu { path: output_dir.clone() })?;
128
129 if let Some(ff) = ff {
130 let cookies = ff
131 .await
132 .context(error::TokioTaskSnafu)??;
133 let out_file = output_dir.join(crate::COOKIES_FILE);
134 let sep = sep.clone();
135
136 utils::write_cookies(out_file, cookies, sep)
137 .await
138 .context(error::TokioTaskSnafu)??;
139 }
140
141 Ok(())
142 }
143}