leetcode_core/graphql/
client.rs1use crate::errors::{AppResult, LcAppError};
2use crate::get_client;
3use async_trait::async_trait;
4use lru::LruCache;
5use serde::{de::DeserializeOwned, Serialize};
6use serde_json::{json, Value};
7use std::collections::hash_map::DefaultHasher;
8use std::hash::Hash;
9use std::hash::Hasher;
10use std::sync::RwLock;
11use std::{num::NonZeroUsize, sync::OnceLock};
12
13pub static CACHE: OnceLock<RwLock<LruCache<u64, String>>> = OnceLock::new();
14
15fn get_cache<'a>() -> &'a RwLock<LruCache<u64, String>> {
16 CACHE.get_or_init(|| RwLock::new(LruCache::new(NonZeroUsize::new(20).unwrap())))
17}
18
19fn hash_string(input: &str) -> u64 {
20 let mut hasher = DefaultHasher::new();
21 input.hash(&mut hasher);
22 hasher.finish()
23}
24
25#[async_trait]
26pub trait GQLLeetcodeRequest: Serialize + Sync {
27 type T: DeserializeOwned;
28
29 fn get_body(&self) -> Value {
30 json!(self)
31 }
32
33 fn is_post(&self) -> bool {
34 true
35 }
36
37 fn get_endpoint(&self) -> String {
39 "https://leetcode.com/graphql".to_string()
40 }
41
42 fn use_cache(&self) -> bool {
43 false
44 }
45
46 fn get_query_hash(&self) -> u64 {
47 hash_string(format!("{}{}", self.get_endpoint(), self.get_body()).as_str())
48 }
49
50 async fn send(&self) -> AppResult<Self::T> {
51 if self.use_cache() {
52 let mut c = get_cache().write().unwrap();
53 if let Some(value) = c.get(&self.get_query_hash()) {
54 return Ok(serde_json::from_str(value.as_str())?);
55 };
56 }
57
58 let request = if self.is_post() {
59 get_client()
60 .post(self.get_endpoint())
61 .json(&self.get_body())
62 } else {
63 get_client().get(self.get_endpoint())
64 };
65 let response = request
66 .header("Content-Type", "application/json")
67 .send()
68 .await?;
69
70 if response.status().as_u16() == 403 {
71 return Err(LcAppError::CookiesExpiredError);
72 } else if response.status().as_u16() != 200 {
73 return Err(LcAppError::StatusCodeError {
74 code: response.status().to_string(),
75 contents: response.text().await?,
76 });
77 }
78 let result = response.text().await?;
79
80 if self.use_cache() {
81 let mut c = get_cache().write().unwrap();
82 c.put(self.get_query_hash(), result.clone());
83 }
84 match serde_json::from_str(result.as_str()) {
85 Ok(parsed_message) => Ok(parsed_message),
86 Err(e) => {
87 log::debug!("{}\n{}", &e, result.as_str());
88 Err(LcAppError::DeserializeError(e))
89 }
90 }
91 }
92}