leetcode_api/leetcode/
headers.rs

1use std::str::FromStr;
2
3use decrypt_cookies::{browser::cookies::LeetCodeCookies, prelude::*};
4use lcode_config::global::G_USER_CONFIG;
5use miette::{IntoDiagnostic, Result, bail};
6use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
7
8/// headers for `LeetCode` reqwest
9///
10/// * `headers`: headers for reqwest
11#[derive(Debug)]
12#[derive(Default)]
13#[derive(Clone)]
14#[derive(PartialEq, Eq)]
15pub struct Headers {
16    pub headers: HeaderMap,
17}
18
19impl Headers {
20    pub async fn build_default() -> Result<Self> {
21        let host = format!("{}.{}", "leetcode", G_USER_CONFIG.config.url_suffix);
22        Self::build(&host).await
23    }
24    pub async fn build(host: &str) -> Result<Self> {
25        let default_headers = HeaderMap::new();
26        let mut cookies = G_USER_CONFIG.cookies.clone();
27        if !cookies.is_completion() {
28            cookies = get_cookie_by_browser(&G_USER_CONFIG.config.browser, host)
29                .await
30                .unwrap_or_default();
31        }
32
33        if !cookies.is_completion() {
34            cookies = get_cookie(host)
35                .await
36                .unwrap_or_else(|err| {
37                    tracing::warn!("{err}");
38                    LeetCodeCookies::default()
39                });
40        }
41
42        let cookie = cookies.to_string();
43
44        let kv_vec: Vec<(&str, &str)> = vec![
45            ("cookie", &cookie),
46            ("x-csrftoken", &cookies.csrf),
47            ("origin", &G_USER_CONFIG.urls.origin),
48        ];
49        let default_headers = Self::mod_headers(default_headers, kv_vec)?;
50
51        Ok(Self { headers: default_headers })
52    }
53
54    /// new or modify headers
55    ///
56    /// * `headers`: be modified headers
57    /// * `kv_vec`: added content
58    pub fn mod_headers(mut headers: HeaderMap, kv_vec: Vec<(&str, &str)>) -> Result<HeaderMap> {
59        for (k, v) in kv_vec {
60            let name = HeaderName::from_str(k).into_diagnostic()?;
61            let value = HeaderValue::from_str(v).into_diagnostic()?;
62
63            headers.insert(name, value);
64        }
65        Ok(headers)
66    }
67}
68
69async fn get_cookie_by_browser(browser: &str, host: &str) -> Result<LeetCodeCookies> {
70    macro_rules! ff_ck {
71        ($ff:ident) => {
72            let Ok(builder) = FirefoxBuilder::<$ff>::new()
73            else {
74                bail!("failed")
75            };
76            let Ok(getter) = builder.build().await
77            else {
78                bail!("failed")
79            };
80            let Ok(ck) = getter.get_session_csrf(host).await
81            else {
82                bail!("failed")
83            };
84            if ck.is_completion() {
85                return Ok(ck);
86            }
87            bail!("failed")
88        };
89    }
90    macro_rules! ch_ck {
91        ($chromium:ident) => {
92            let Ok(getter) = ChromiumBuilder::<$chromium>::new()
93                .build()
94                .await
95            else {
96                bail!("failed")
97            };
98            let Ok(ck) = getter
99                .get_cookies_session_csrf(host)
100                .await
101            else {
102                bail!("failed")
103            };
104            if ck.is_completion() {
105                return Ok(ck);
106            }
107            bail!("failed")
108        };
109    }
110
111    match browser.to_lowercase().as_str() {
112        "firefox" => {
113            ff_ck!(Firefox);
114        },
115        "librewolf" => {
116            ff_ck!(Librewolf);
117        },
118        "chrome" => {
119            ch_ck!(Chrome);
120        },
121        "edge" => {
122            ch_ck!(Edge);
123        },
124        "chromium" => {
125            ch_ck!(Chromium);
126        },
127        "brave" => {
128            ch_ck!(Brave);
129        },
130        "vivaldi" => {
131            ch_ck!(Vivaldi);
132        },
133        "yandex" => {
134            ch_ck!(Yandex);
135        },
136        "opera" => {
137            ch_ck!(Opera);
138        },
139        #[cfg(not(target_os = "linux"))]
140        "operagx" => {
141            ch_ck!(OperaGX);
142        },
143        #[cfg(not(target_os = "linux"))]
144        "coccoc" => {
145            ch_ck!(CocCoc);
146        },
147        _ => {
148            bail!("failed")
149        },
150    }
151}
152
153async fn get_cookie(host: &str) -> Result<LeetCodeCookies> {
154    macro_rules! ffs {
155        ($($ff:ident), *,) => {
156            $(
157                'outer: {
158                    let Ok(builder) = FirefoxBuilder::<$ff>::new()
159                    else {
160                        break 'outer;
161                    };
162                    let Ok(getter) = builder.build().await
163                    else {
164                        break 'outer;
165                    };
166                    let Ok(ck) = getter.get_session_csrf(host).await
167                    else {
168                        break 'outer;
169                    };
170                    if ck.is_completion() {
171                        return Ok(ck);
172                    }
173                }
174            )*
175        };
176    }
177    ffs!(Firefox, Librewolf,);
178
179    macro_rules! chromiums {
180        ($($chromium:ident), *,) => {
181            $(
182                'outer: {
183                    let Ok(chromium) = ChromiumBuilder::<$chromium>::new()
184                        .build()
185                        .await
186                    else {
187                        break 'outer;
188                    };
189                    let Ok(ck) = chromium
190                        .get_cookies_session_csrf(host)
191                        .await
192                    else {
193                        break 'outer;
194                    };
195                    if ck.is_completion() {
196                        return Ok(ck);
197                    }
198                }
199            )*
200        };
201    }
202    chromiums!(Chrome, Edge, Chromium, Brave, Yandex, Vivaldi, Opera,);
203    #[cfg(not(target_os = "linux"))]
204    chromiums!(OperaGX, CocCoc,);
205
206    bail!("failed")
207}