leetcode_cli/cache/
parser.rs

1//! Sub-Module for parsing resp data
2use super::models::*;
3use serde_json::Value;
4
5/// problem parser
6pub fn problem(problems: &mut Vec<Problem>, v: Value) -> Option<()> {
7    let pairs = v.get("stat_status_pairs")?.as_array()?;
8    for p in pairs {
9        let stat = p.get("stat")?.as_object()?;
10        let total_acs = stat.get("total_acs")?.as_f64()? as f32;
11        let total_submitted = stat.get("total_submitted")?.as_f64()? as f32;
12
13        let fid_obj = stat.get("frontend_question_id")?;
14        let fid = match fid_obj.as_i64() {
15            // Handle on leetcode-com
16            Some(s) => s as i32,
17            // Handle on leetcode-cn
18            None => fid_obj.as_str()?.split(' ').last()?.parse::<i32>().ok()?,
19        };
20
21        problems.push(Problem {
22            category: v.get("category_slug")?.as_str()?.to_string(),
23            fid,
24            id: stat.get("question_id")?.as_i64()? as i32,
25            level: p.get("difficulty")?.as_object()?.get("level")?.as_i64()? as i32,
26            locked: p.get("paid_only")?.as_bool()?,
27            name: stat.get("question__title")?.as_str()?.to_string(),
28            percent: total_acs / total_submitted * 100.0,
29            slug: stat.get("question__title_slug")?.as_str()?.to_string(),
30            starred: p.get("is_favor")?.as_bool()?,
31            status: p.get("status")?.as_str().unwrap_or("Null").to_string(),
32            desc: String::new(),
33        });
34    }
35
36    Some(())
37}
38
39/// desc parser
40pub fn desc(q: &mut Question, v: Value) -> Option<bool> {
41    /* None - parsing failed
42     * Some(false) - content was null (premium?)
43     * Some(true) - content was parsed
44     */
45    let o = &v
46        .as_object()?
47        .get("data")?
48        .as_object()?
49        .get("question")?
50        .as_object()?;
51
52    if *o.get("content")? == Value::Null {
53        return Some(false);
54    }
55
56    *q = Question {
57        content: o.get("content")?.as_str().unwrap_or("").to_string(),
58        stats: serde_json::from_str(o.get("stats")?.as_str()?).ok()?,
59        defs: serde_json::from_str(o.get("codeDefinition")?.as_str()?).ok()?,
60        case: o.get("sampleTestCase")?.as_str()?.to_string(),
61        all_cases: o
62            .get("exampleTestcases")
63            .unwrap_or(o.get("sampleTestCase")?) // soft fail to the sampleTestCase
64            .as_str()?
65            .to_string(),
66        metadata: serde_json::from_str(o.get("metaData")?.as_str()?).ok()?,
67        test: o.get("enableRunCode")?.as_bool()?,
68        t_content: o
69            .get("translatedContent")?
70            .as_str()
71            .unwrap_or("")
72            .to_string(),
73    };
74
75    Some(true)
76}
77
78/// tag parser
79pub fn tags(v: Value) -> Option<Vec<String>> {
80    trace!("Parse tags...");
81    let tag = v.as_object()?.get("data")?.as_object()?.get("topicTag")?;
82
83    if tag.is_null() {
84        return Some(vec![]);
85    }
86
87    let arr = tag.as_object()?.get("questions")?.as_array()?;
88
89    let mut res: Vec<String> = vec![];
90    for q in arr.iter() {
91        res.push(q.as_object()?.get("questionId")?.as_str()?.to_string())
92    }
93
94    Some(res)
95}
96
97/// daily parser
98pub fn daily(v: Value) -> Option<i32> {
99    trace!("Parse daily...");
100    let v_obj = v.as_object()?.get("data")?.as_object()?;
101    match v_obj.get("activeDailyCodingChallengeQuestion") {
102        // Handle on leetcode-com
103        Some(v) => v,
104        // Handle on leetcode-cn
105        None => v_obj.get("todayRecord")?.as_array()?.first()?,
106    }
107    .as_object()?
108    .get("question")?
109    .as_object()?
110    .get("questionFrontendId")?
111    .as_str()?
112    .parse()
113    .ok()
114}
115
116/// user parser
117pub fn user(v: Value) -> Option<Option<(String, bool)>> {
118    // None => error while parsing
119    // Some(None) => User not found
120    // Some("...") => username
121    let user = v.as_object()?.get("data")?.as_object()?.get("user")?;
122    if *user == Value::Null {
123        return Some(None);
124    }
125    let user = user.as_object()?;
126    Some(Some((
127        user.get("username")?.as_str()?.to_owned(),
128        user.get("isCurrentUserPremium")?.as_bool()?,
129    )))
130}
131
132pub use ss::ssr;
133/// string or squence
134mod ss {
135    use serde::{de, Deserialize, Deserializer};
136    use std::fmt;
137    use std::marker::PhantomData;
138
139    /// de Vec<String> from string or sequence
140    pub fn ssr<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
141    where
142        D: Deserializer<'de>,
143    {
144        struct StringOrVec(PhantomData<Vec<String>>);
145
146        impl<'de> de::Visitor<'de> for StringOrVec {
147            type Value = Vec<String>;
148
149            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
150                formatter.write_str("string or list of strings")
151            }
152
153            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
154            where
155                E: de::Error,
156            {
157                Ok(vec![value.to_owned()])
158            }
159
160            fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
161            where
162                S: de::SeqAccess<'de>,
163            {
164                Deserialize::deserialize(de::value::SeqAccessDeserializer::new(visitor))
165            }
166        }
167
168        deserializer.deserialize_any(StringOrVec(PhantomData))
169    }
170}