tidy_browser/
firefox.rs

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                                // TODO:
140                            }
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}