leetcode_api/leetcode/
mod.rs1mod 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::{miette, IntoDiagnostic, Result};
10use reqwest::{
11 header::{HeaderMap, HeaderValue},
12 Client, ClientBuilder,
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
31pub static TOTAL_QS_INDEX_NUM: AtomicU32 = AtomicU32::new(0);
33pub static CUR_QS_INDEX_NUM: AtomicU32 = AtomicU32::new(0);
35
36pub static TOTAL_TOPIC_QS_INDEX_NUM: AtomicU32 = AtomicU32::new(0);
38pub 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#[derive(Default)]
60#[derive(Debug)]
61#[derive(Clone)]
62pub struct LeetCode {
63 pub client: Client,
64 pub headers: HeaderMap,
65}
66
67impl LeetCode {
68 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}