Skip to content

Commit d8e07e6

Browse files
committed
feat: Add full user integration manage
1 parent d0911d7 commit d8e07e6

File tree

9 files changed

+325
-32
lines changed

9 files changed

+325
-32
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "leetcoderustapi"
3-
version = "0.1.7"
3+
version = "1.0.0"
44
authors = ["Kirill Melkozerov <k.melkozerov@gmail.com>"]
55
edition = "2021"
66
license = "MIT"

README.md

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This Rust library provides a convenient way to interact with the LeetCode API, a
1111
Add the following line to your `Cargo.toml` file:
1212
```toml
1313
[dependencies]
14-
leetcoderustapi = "0.1.7"
14+
leetcoderustapi = "1.0.0"
1515
```
1616
## Usage
1717
### Authentication
@@ -96,11 +96,6 @@ async fn main() {
9696
.send_test("rust", "impl Solution { fn two_sum() {}}")
9797
.await
9898
.unwrap();
99-
100-
// And we can check public stats of the searched profile
101-
let public_user_data = api.search_public_user("1101-1").await.unwrap();
102-
103-
10499
}
105100
```
106101

@@ -151,7 +146,52 @@ async fn main() {
151146
let notifications = user_profile.get_notifications().await.unwrap();
152147
}
153148
```
149+
### Example: Actions with Public user profile
150+
```rust
151+
#[tokio::main]
152+
async fn main() {
153+
// Set cookie from leetcode
154+
let token = std::env::var("COOKIE").expect("cookie doesn't set");
155+
156+
// Create a new LeetCode client
157+
let api = UserApi::new(&token).await.unwrap();
154158

159+
// Find public user
160+
let user = api
161+
.find_profile("1101-1")
162+
.await;
163+
164+
// Check public user common stats
165+
let user_stats = user
166+
.recent_subm_list()
167+
.await
168+
.unwrap();
169+
170+
// Check what langs used user
171+
let lang_stats = user
172+
.language_stats()
173+
.await
174+
.unwrap();
175+
176+
// Check what problems (by tags) solve user
177+
let skill_stats = user
178+
.skill_stats()
179+
.await
180+
.unwrap();
181+
182+
// Show rating by beating problems
183+
let beat_stats = ser
184+
.problem_beat_stats()
185+
.await
186+
.unwrap();
187+
188+
// Show recent submissons of user
189+
let beat_stats = ser
190+
.recent_subm_list()
191+
.await
192+
.unwrap();
193+
}
194+
```
155195

156196

157197
#### Important

src/lib.rs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use error::Errors;
22
use problem_actions::Problem;
33
use problem_build::{Filters, ProblemBuilder};
4-
use profile::MyProfile;
4+
use profile::{MyProfile, UserProfile};
55
use reqwest::header::{HeaderMap, HeaderValue};
66
use resources::{
77
cookie::CookieData, descr::ProblemData, fav_list::FavoriteList,
8-
problemfulldata::ProblemFullData, pub_data_profile::UserFoundData,
8+
problemfulldata::ProblemFullData,
99
};
1010
use serde_json::{json, Value};
1111

@@ -230,27 +230,11 @@ impl UserApi {
230230
})
231231
}
232232

233-
pub async fn search_public_user(&self, profile_name: &str) -> Result<UserFoundData, Errors> {
234-
let query = json!({
235-
"query": "query userPublicProfile($username: String!) {\n matchedUser(username: $username) {\n contestBadge {\n name\n expired\n hoverText\n icon\n }\n username\n githubUrl\n twitterUrl\n linkedinUrl\n profile {\n ranking\n userAvatar\n realName\n aboutMe\n school\n websites\n countryName\n company\n jobTitle\n skillTags\n postViewCount\n postViewCountDiff\n reputation\n reputationDiff\n solutionCount\n solutionCountDiff\n categoryDiscussCount\n categoryDiscussCountDiff\n }\n }\n}",
236-
"variables": {
237-
"username": profile_name
238-
},
239-
"operationName": "userPublicProfile"
240-
});
241-
242-
let query = serde_json::to_string(&query).unwrap();
243-
244-
let data_info = self
245-
.client
246-
.post("https://leetcode.com/graphql/")
247-
.body(query)
248-
.send()
249-
.await?
250-
.text()
251-
.await?;
252-
253-
Ok(serde_json::from_str::<UserFoundData>(&data_info)?)
233+
pub async fn find_profile(&self, username: &str) -> UserProfile {
234+
UserProfile {
235+
client: self.client.clone(),
236+
username: String::from(username),
237+
}
254238
}
255239

256240
async fn fetch_fav_list_data(&self) -> Result<FavoriteList, Errors> {

src/profile.rs

Lines changed: 150 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use serde_json::json;
33
use crate::{
44
error::Errors,
55
resources::{
6-
data_profile::ProfileData, fav_list::FavoriteList, notification::NotificationsData,
6+
beat_stats::BeatStats, data_profile::ProfileData, fav_list::FavoriteList,
7+
lang_stats::LanguageStats, notification::NotificationsData,
8+
pub_data_profile::UserFoundData, skill_stats::SkillStats, subm_list::RecentSubmList,
79
},
810
};
911

@@ -227,7 +229,7 @@ impl MyProfile {
227229
"operationName": operation_name
228230
});
229231

230-
let query = serde_json::to_string(&json_data).unwrap();
232+
let query = serde_json::to_string(&json_data)?;
231233

232234
let data_info = self
233235
.client
@@ -241,3 +243,149 @@ impl MyProfile {
241243
Ok(serde_json::from_str::<ProfileData>(&data_info)?)
242244
}
243245
}
246+
247+
#[derive(Debug)]
248+
pub struct UserProfile {
249+
pub(crate) client: reqwest::Client,
250+
pub(crate) username: String,
251+
}
252+
253+
impl UserProfile {
254+
pub async fn user_stats(&self) -> Result<UserFoundData, Errors> {
255+
let query = json!({
256+
"query": "query userPublicProfile($username: String!) {\n matchedUser(username: $username) {\n contestBadge {\n name\n expired\n hoverText\n icon\n }\n username\n githubUrl\n twitterUrl\n linkedinUrl\n profile {\n ranking\n userAvatar\n realName\n aboutMe\n school\n websites\n countryName\n company\n jobTitle\n skillTags\n postViewCount\n postViewCountDiff\n reputation\n reputationDiff\n solutionCount\n solutionCountDiff\n categoryDiscussCount\n categoryDiscussCountDiff\n }\n }\n}",
257+
"variables": {
258+
"username": self.username
259+
},
260+
"operationName": "userPublicProfile"
261+
});
262+
263+
let query = serde_json::to_string(&query)?;
264+
265+
let data_info = self
266+
.client
267+
.post("https://leetcode.com/graphql/")
268+
.body(query)
269+
.send()
270+
.await?
271+
.text()
272+
.await?;
273+
274+
Ok(serde_json::from_str::<UserFoundData>(&data_info)?)
275+
}
276+
277+
pub async fn language_stats(&self) -> Result<LanguageStats, Errors> {
278+
let query = json!({
279+
"query": "query languageStats($username: String!) {\n matchedUser(username: $username) {\n languageProblemCount {\n languageName\n problemsSolved\n }\n }\n}",
280+
"variables": {
281+
"username": self.username
282+
},
283+
"operationName": "languageStats"
284+
});
285+
286+
let query = serde_json::to_string(&query)?;
287+
288+
let data_info = self
289+
.client
290+
.post("https://leetcode.com/graphql/")
291+
.body(query)
292+
.send()
293+
.await?
294+
.text()
295+
.await?;
296+
297+
Ok(serde_json::from_str::<LanguageStats>(&data_info)?)
298+
}
299+
300+
pub async fn skill_stats(&self) -> Result<SkillStats, Errors> {
301+
let query = json!({
302+
"query": r#"
303+
query skillStats($username: String!) {
304+
matchedUser(username: $username) {
305+
tagProblemCounts {
306+
advanced {
307+
tagName
308+
tagSlug
309+
problemsSolved
310+
}
311+
intermediate {
312+
tagName
313+
tagSlug
314+
problemsSolved
315+
}
316+
fundamental {
317+
tagName
318+
tagSlug
319+
problemsSolved
320+
}
321+
}
322+
}
323+
}
324+
"#,
325+
"variables": {
326+
"username": "1101-1"
327+
},
328+
"operationName": "skillStats"
329+
});
330+
331+
let query = serde_json::to_string(&query)?;
332+
333+
let data_info = self
334+
.client
335+
.post("https://leetcode.com/graphql/")
336+
.body(query)
337+
.send()
338+
.await?
339+
.text()
340+
.await?;
341+
342+
Ok(serde_json::from_str::<SkillStats>(&data_info)?)
343+
}
344+
345+
pub async fn problem_beat_stats(&self) -> Result<BeatStats, Errors> {
346+
let query = json!({
347+
"query": "query userProblemsSolved($username: String!) {\n matchedUser(username: $username) {\n problemsSolvedBeatsStats {\n difficulty\n percentage\n }\n submitStatsGlobal {\n acSubmissionNum {\n difficulty\n count\n }\n }\n }\n}",
348+
"variables": {
349+
"username": self.username
350+
},
351+
"operationName": "userProblemsSolved"
352+
});
353+
354+
let query = serde_json::to_string(&query)?;
355+
356+
let data_info = self
357+
.client
358+
.post("https://leetcode.com/graphql/")
359+
.body(query)
360+
.send()
361+
.await?
362+
.text()
363+
.await?;
364+
365+
Ok(serde_json::from_str::<BeatStats>(&data_info)?)
366+
}
367+
368+
pub async fn recent_subm_list(&self) -> Result<RecentSubmList, Errors> {
369+
let query = json!({
370+
"query": "query recentAcSubmissions($username: String!, $limit: Int!) {\n recentAcSubmissionList(username: $username, limit: $limit) {\n id\n title\n titleSlug\n timestamp\n }\n}",
371+
"variables": {
372+
"username": self.username,
373+
"limit": 15
374+
},
375+
"operationName": "recentAcSubmissions"
376+
});
377+
378+
let query = serde_json::to_string(&query)?;
379+
380+
let data_info = self
381+
.client
382+
.post("https://leetcode.com/graphql/")
383+
.body(query)
384+
.send()
385+
.await?
386+
.text()
387+
.await?;
388+
389+
Ok(serde_json::from_str::<RecentSubmList>(&data_info)?)
390+
}
391+
}

src/resources/beat_stats.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use serde::Deserialize;
2+
3+
#[derive(Debug, Deserialize)]
4+
pub struct ProblemsSolvedBeatsStats {
5+
pub difficulty: String,
6+
pub percentage: f32,
7+
}
8+
9+
#[derive(Debug, Deserialize)]
10+
pub struct AcSubmissionNum {
11+
pub difficulty: String,
12+
pub count: i32,
13+
}
14+
15+
#[allow(non_snake_case)]
16+
#[derive(Debug, Deserialize)]
17+
pub struct SubmitStatsGlobal {
18+
pub acSubmissionNum: Vec<AcSubmissionNum>,
19+
}
20+
21+
#[allow(non_snake_case)]
22+
#[derive(Debug, Deserialize)]
23+
pub struct MatchedUser {
24+
pub problemsSolvedBeatsStats: Vec<ProblemsSolvedBeatsStats>,
25+
pub submitStatsGlobal: SubmitStatsGlobal,
26+
}
27+
28+
#[allow(non_snake_case)]
29+
#[derive(Debug, Deserialize)]
30+
pub struct Data {
31+
pub matchedUser: MatchedUser,
32+
}
33+
34+
#[derive(Debug, Deserialize)]
35+
pub struct BeatStats {
36+
pub data: Data,
37+
}

src/resources/lang_stats.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use serde::Deserialize;
2+
3+
#[allow(non_snake_case)]
4+
#[derive(Debug, Deserialize)]
5+
pub struct LanguageProblemCount {
6+
pub languageName: String,
7+
pub problemsSolved: i32,
8+
}
9+
10+
#[allow(non_snake_case)]
11+
#[derive(Debug, Deserialize)]
12+
pub struct MatchedUser {
13+
pub languageProblemCount: Vec<LanguageProblemCount>,
14+
}
15+
16+
#[allow(non_snake_case)]
17+
#[derive(Debug, Deserialize)]
18+
pub struct Data {
19+
pub matchedUser: MatchedUser,
20+
}
21+
22+
#[derive(Debug, Deserialize)]
23+
pub struct LanguageStats {
24+
pub data: Data,
25+
}

src/resources/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
pub mod beat_stats;
12
pub mod cookie;
23
pub mod data_profile;
34
pub mod descr;
45
pub mod fav_list;
6+
pub mod lang_stats;
57
pub mod notification;
68
pub mod problemfulldata;
79
pub mod pub_data_profile;
10+
pub mod skill_stats;
11+
pub mod subm_list;
812
pub mod subm_send;
913
pub mod subm_show;
1014
pub mod test_send;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy