Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Jul 7, 2024. It is now read-only.

Commit 1d32cfc

Browse files
committed
use serde_json to autoparse where possible
1 parent eefa2f0 commit 1d32cfc

File tree

3 files changed

+68
-42
lines changed

3 files changed

+68
-42
lines changed

‎src/cache/models.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,22 @@ pub struct ContestQuestionStub {
2323
pub title_slug: String,
2424
}
2525
/// Contest model
26-
#[derive(Debug, Serialize,Deserialize, Clone)]
26+
#[derive(Debug, Deserialize, Clone)]
2727
pub struct Contest {
2828
pub id: i32,
2929
pub duration: i32,
3030
pub start_time: i64,
3131
pub title: String,
3232
pub title_slug: String,
33+
#[serde(skip)] // the description is not used at the moment,
34+
#[serde(default)] // so we use an empty string instead
3335
pub description: String,
3436
pub is_virtual: bool,
37+
#[serde(skip)]
3538
pub contains_premium: bool,
39+
#[serde(skip)]
3640
pub registered: bool,
41+
#[serde(skip)]
3742
pub questions: Vec<ContestQuestionStub>,
3843
}
3944
// TODO: improve Display for Contest*
@@ -46,20 +51,50 @@ impl std::fmt::Display for Contest {
4651
}
4752
}
4853

54+
fn deserialize_string<'de,D,T>(deserializer: D) -> Result<T, D::Error>
55+
where D: serde::Deserializer<'de>,
56+
T: std::str::FromStr {
57+
let s: String = Deserialize::deserialize(deserializer)?;
58+
s.parse::<T>().map_err(|_| serde::de::Error::invalid_value(
59+
serde::de::Unexpected::Str(&s),
60+
&"valid string"
61+
))
62+
}
63+
4964
/// Problem model
50-
#[derive(AsChangeset, Clone, Identifiable, Insertable, Queryable, Serialize, Debug)]
65+
#[derive(AsChangeset, Clone, Identifiable, Insertable, Queryable, Deserialize, Debug)]
5166
#[table_name = "problems"]
5267
pub struct Problem {
68+
#[serde(alias="category_slug")]
69+
#[serde(alias="categoryTitle")]
5370
pub category: String,
71+
#[serde(alias="frontend_question_id")]
72+
#[serde(alias="questionFrontendId")]
73+
#[serde(deserialize_with="deserialize_string")]
5474
pub fid: i32,
75+
#[serde(alias="question_id")]
76+
#[serde(alias="questionId")]
77+
#[serde(deserialize_with="deserialize_string")]
5578
pub id: i32,
79+
#[serde(skip)]
5680
pub level: i32,
81+
#[serde(alias="paid_only")]
82+
#[serde(alias="isPaidOnly")]
5783
pub locked: bool,
84+
#[serde(alias="question__title")]
85+
#[serde(alias="title")]
5886
pub name: String,
87+
#[serde(skip)]
5988
pub percent: f32,
89+
#[serde(alias="question__title_slug")]
90+
#[serde(alias="titleSlug")]
6091
pub slug: String,
92+
#[serde(alias="is_favor")]
93+
#[serde(alias="isFavor")]
6194
pub starred: bool,
95+
#[serde(skip)]
6296
pub status: String,
97+
#[serde(skip)]
6398
pub desc: String,
6499
}
65100

‎src/cache/parser.rs

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,20 @@
11
//! Sub-Module for parsing resp data
22
use super::models::*;
3-
use serde_json::Value;
3+
use serde_json::{Value,from_value};
4+
45

56
/// contest parser
67
pub fn contest(v: Value) -> Option<Contest> {
78
let o = v.as_object()?;
8-
let contest = o.get("contest")?.as_object()?;
9-
let questions: Vec<ContestQuestionStub> = o
10-
.get("questions")?.as_array()?
11-
.iter().map(|q| {
12-
let stub: Result<ContestQuestionStub, _> = serde_json::from_value(q.clone());
13-
stub.unwrap()
14-
}).collect();
15-
Some(Contest {
16-
id: contest.get("id")?.as_i64()? as i32,
17-
duration: contest.get("duration")?.as_i64()? as i32,
18-
start_time: contest.get("start_time")?.as_i64()?,
19-
title: contest.get("title")?.as_str()?.to_string(),
20-
title_slug: contest.get("title_slug")?.as_str()?.to_owned(),
21-
description: "".to_owned(), // TODO: display description. contest.get("description")?.as_str()?.to_owned(),
22-
is_virtual: contest.get("is_virtual")?.as_bool()?,
23-
contains_premium: o.get("containsPremium")?.as_bool()?,
24-
registered: o.get("registered")?.as_bool()?,
25-
questions
26-
})
9+
let mut contest: Contest = from_value(
10+
o.get("contest")?.clone()
11+
).ok()?;
12+
contest.questions = from_value(
13+
o.get("questions")?.clone()
14+
).ok()?;
15+
contest.contains_premium = o.get("containsPremium")?.as_bool()?;
16+
contest.registered = o.get("registered")?.as_bool()?;
17+
Some(contest)
2718
}
2819

2920
/// problem parser
@@ -55,31 +46,29 @@ pub fn problem(problems: &mut Vec<Problem>, v: Value) -> Option<()> {
5546
// TODO: implement test for this
5647
/// graphql problem && question parser
5748
pub fn graphql_problem_and_question(v: Value) -> Option<(Problem,Question)> {
49+
// parse top-level data from API
5850
let mut qn = Question::default();
5951
assert_eq!(Some(true), desc(&mut qn, v.clone()));
6052
let percent = &qn.stats.rate;
6153
let percent = percent[..percent.len()-1].parse::<f32>().ok()?;
54+
55+
// parse v.question specifically
6256
let v = v.as_object()?.get("data")?
63-
.as_object()?.get("question")?
64-
.as_object()?;
65-
Some((Problem {
66-
category: v.get("categoryTitle")?.as_str()?.to_ascii_lowercase(), // dangerous, since this is not actually the slug. But currently (May 2022) ok
67-
fid: v.get("questionFrontendId")?.as_str()?.parse().ok()?,
68-
id: v.get("questionId")?.as_str()?.parse().ok()?,
69-
level: match v.get("difficulty")?.as_str()?.chars().next()? {
70-
'E' => 1,
71-
'M' => 2,
72-
'H' => 3,
73-
_ => 0,
74-
},
75-
locked: false, // lazy
76-
name: v.get("title")?.as_str()?.to_string(),
77-
percent,
78-
slug: v.get("titleSlug")?.as_str()?.to_string(),
79-
starred: v.get("isFavor")?.as_bool()?,
80-
status: v.get("status")?.as_str().unwrap_or("Null").to_owned(),
81-
desc: serde_json::to_string(&qn).ok()?,
82-
}, qn))
57+
.as_object()?.get("question")?;
58+
let mut p: Problem = from_value(v.clone()).unwrap();
59+
p.percent = percent;
60+
p.level = match v.as_object()?.get("difficulty")?.as_str()?.chars().next()? {
61+
'E' => 1,
62+
'M' => 2,
63+
'H' => 3,
64+
_ => 0,
65+
};
66+
p.status = v.get("status")?.as_str().unwrap_or("Null").to_owned();
67+
p.desc = serde_json::to_string(&qn).ok()?;
68+
/* The graphql API doesn't return the category slug, only the printed category title. */
69+
p.category = p.category.to_ascii_lowercase(); // Currently working (June 2022)
70+
/* But lowercasing is stupid. This will break if a category with whitespaces appears. */
71+
Some((p,qn))
8372
}
8473

8574
/// desc parser

‎src/plugins/leetcode.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ impl LeetCode {
216216
#[named]
217217
pub async fn get_contest_info(&self, contest: &str) -> Result<Response, Error> {
218218
trace!("Requesting {} detail...", contest);
219+
// cannot use the graphql API here because it does not provide registration status
219220
let url = &self.conf.sys.urls
220221
.get("contest_info")
221222
.ok_or(Error::NoneError)?
@@ -243,6 +244,7 @@ impl LeetCode {
243244
lang
244245
code
245246
}
247+
isPaidOnly
246248
exampleTestcases
247249
sampleTestCase
248250
enableRunCode

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /