leetcode_api/leetcode/
mod.rs

1mod graphqls;
2pub mod headers;
3pub mod impl_lc;
4pub mod question;
5pub mod resps;
6
7use std::{fmt::Display, sync::atomic::AtomicU32, time::Duration};
8
9use miette::{IntoDiagnostic, Result, miette};
10use reqwest::{
11    Client, ClientBuilder,
12    header::{HeaderMap, HeaderValue},
13};
14use serde::de::DeserializeOwned;
15use tracing::{debug, trace};
16
17use self::headers::Headers;
18use crate::Json;
19
20pub const CATEGORIES: [&str; 8] = [
21    "algorithms",
22    "concurrency",
23    "database",
24    "javascript",
25    "lcci",
26    "lcof",
27    "pandas",
28    "shell",
29];
30
31/// for progress bar, total insert num
32pub static TOTAL_QS_INDEX_NUM: AtomicU32 = AtomicU32::new(0);
33/// for progress bar, current inserted num
34pub static CUR_QS_INDEX_NUM: AtomicU32 = AtomicU32::new(0);
35
36/// for progress bar, total insert num
37pub static TOTAL_TOPIC_QS_INDEX_NUM: AtomicU32 = AtomicU32::new(0);
38/// for progress bar, current inserted num
39pub static CUR_TOPIC_QS_INDEX_NUM: AtomicU32 = AtomicU32::new(0);
40
41#[derive(Clone)]
42#[derive(Debug)]
43#[derive(PartialEq, Eq)]
44pub enum IdSlug {
45    Id(u32),
46    Slug(String),
47}
48
49impl Display for IdSlug {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        match self {
52            Self::Id(num) => num.fmt(f),
53            Self::Slug(slug) => slug.fmt(f),
54        }
55    }
56}
57
58/// interact with leetcode.com/cn
59#[derive(Default)]
60#[derive(Debug)]
61#[derive(Clone)]
62pub struct LeetCode {
63    pub client: Client,
64    pub headers: HeaderMap,
65}
66
67impl LeetCode {
68    /// Create a `LeetCode` instance and initialize some variables
69    pub async fn build() -> Self {
70        let client = ClientBuilder::new()
71            .brotli(true)
72            .connect_timeout(Duration::from_secs(30))
73            .build()
74            .unwrap_or_default();
75
76        Self {
77            client,
78            headers: Headers::build_default()
79                .await
80                .unwrap_or_default()
81                .headers,
82        }
83    }
84
85    async fn request<T>(
86        &self,
87        url: &str,
88        json: Option<&Json>,
89        headers: HeaderMap<HeaderValue>,
90    ) -> Result<T>
91    where
92        T: DeserializeOwned,
93    {
94        let headers = Headers::mod_headers(headers, vec![("Referer", url)])?;
95
96        let req_builder = json.map_or_else(
97            || self.client.get(url),
98            |json| self.client.post(url).json(json),
99        );
100
101        let resp = req_builder
102            .headers(headers)
103            .send()
104            .await
105            .into_diagnostic()?;
106        trace!("respond: {:#?}", resp);
107        debug!("http status code: {:#?}", resp.status());
108
109        match resp.status().as_u16() {
110            403 => miette::bail!("Forbidden, maybe you not verify email or phone number"),
111            408 => miette::bail!("Request Time-out"),
112            429 => miette::bail!("Your submissions are too frequent."),
113            400..500 => miette::bail!("Client error, HTTP Code: {}", resp.status()),
114            500..600 => miette::bail!("Server error, HTTP Code: {}", resp.status()),
115            _ => {},
116        }
117
118        resp.json::<T>()
119            .await
120            .map_err(|e| miette!("Error: {e}."))
121    }
122}