leetcoderustapi/
problem_build.rs

1use serde::Deserialize;
2use serde_json::json;
3
4use crate::{error::Errors, resources::descr::ProblemData};
5
6#[derive(Debug)]
7pub struct ProblemBuilder {
8    pub(crate) client: reqwest::Client,
9    pub(crate) key_word: String,
10    pub(crate) limit: u32,
11    pub(crate) category: String,
12    pub(crate) filters: Filters,
13}
14
15#[allow(non_snake_case)]
16#[derive(Debug, Deserialize)]
17pub struct Filters {
18    pub difficulty: String,
19    pub orderBy: String,
20    pub sortOrder: String,
21    pub status: String,
22    pub tags: Vec<String>,
23}
24
25impl Default for Filters {
26    fn default() -> Self {
27        Self {
28            difficulty: Default::default(),
29            status: Default::default(),
30            tags: Vec::<String>::new(),
31            orderBy: String::from("FRONTEND_ID"),
32            sortOrder: String::from("ASCENDING"),
33        }
34    }
35}
36
37impl ProblemBuilder {
38    pub fn set_category(mut self, categoty: Category) -> ProblemBuilder {
39        match categoty {
40            Category::AllTopics => self.category = String::from(""),
41            Category::Algorithms => self.category = String::from("algorithms"),
42            Category::DataBase => self.category = String::from("database"),
43            Category::JavaScript => self.category = String::from("javascript"),
44            Category::Shell => self.category = String::from("shell"),
45            Category::Concurrency => self.category = String::from("concurrency"),
46        }
47        self
48    }
49
50    pub fn set_difficulty(mut self, difficulty: Difficulty) -> ProblemBuilder {
51        match difficulty {
52            Difficulty::Easy => self.filters.difficulty = String::from("EASY"),
53            Difficulty::Medium => self.filters.difficulty = String::from("MEDIUM"),
54            Difficulty::Hard => self.filters.difficulty = String::from("HARD"),
55        }
56        self
57    }
58
59    pub fn set_status(mut self, status: Status) -> ProblemBuilder {
60        match status {
61            Status::Todo => self.filters.status = String::from("NOT_STARTED"),
62            Status::Solved => self.filters.status = String::from("AC"),
63            Status::Attempted => self.filters.status = String::from("TRIED"),
64        }
65        self
66    }
67
68    pub fn set_note_limit(mut self, limit: u32) -> ProblemBuilder {
69        self.limit = limit;
70        self
71    }
72
73    pub fn set_keyword(mut self, keyword: &str) -> ProblemBuilder {
74        self.key_word = String::from(keyword);
75        self
76    }
77
78    pub fn set_tags(mut self, tags: Vec<Tags>) -> ProblemBuilder {
79        let mut res_tags = Vec::new();
80
81        for tag in tags {
82            let tag = match tag {
83                Tags::Array => "array",
84                Tags::String => "string",
85                Tags::HashTable => "hash-table",
86                Tags::Math => "math",
87                Tags::DynamicProgramming => "dynamic-programming",
88                Tags::Sorting => "sorting",
89                Tags::Greedy => "greedy",
90                Tags::DepthFirstSearch => "depth-first-search",
91                Tags::Database => "database",
92                Tags::BinarySearch => "binary-search",
93                Tags::BreadthFirstSearch => "breadth-first-search",
94                Tags::Tree => "tree",
95                Tags::Matrix => "matrix",
96                Tags::TwoPointers => "two-pointers",
97                Tags::BinaryTree => "binary-tree",
98                Tags::BitManipulation => "bit-manipulation",
99                Tags::HeapPriorityQueue => "heap-priority-queue",
100                Tags::Stack => "stack",
101                Tags::Graph => "graph",
102                Tags::PrefixSum => "prefix-sum",
103                Tags::Simulation => "simulation",
104                Tags::Design => "design",
105                Tags::Counting => "counting",
106                Tags::Backtracking => "backtracking",
107                Tags::SlidingWindow => "sliding-window",
108                Tags::UnionFind => "union-find",
109                Tags::LinkedList => "linked-list",
110                Tags::OrderedSet => "ordered-set",
111                Tags::MonotonicStack => "monotonic-stack",
112                Tags::Enumeration => "enumeration",
113                Tags::Recursion => "recursion",
114                Tags::Trie => "trie",
115                Tags::DivideAndConquer => "divide-and-conquer",
116                Tags::Bitmask => "bitmask",
117                Tags::BinarySearchTree => "binary-search-tree",
118                Tags::NumberTheory => "number-theory",
119                Tags::Queue => "queue",
120                Tags::SegmentTree => "segment-tree",
121                Tags::Memoization => "memoization",
122                Tags::Geometry => "geometry",
123                Tags::TopologicalSort => "topological-sort",
124                Tags::BinaryIndexedTree => "binary-indexed-tree",
125                Tags::HashFunction => "hash-function",
126                Tags::GameTheory => "game-theory",
127                Tags::ShortestPath => "shortest-path",
128                Tags::Combinatorics => "combinatorics",
129                Tags::DataStream => "data-stream",
130                Tags::Interactive => "interactive",
131                Tags::StringMatching => "string-matching",
132                Tags::RollingHash => "rolling-hash",
133                Tags::Brainteaser => "brainteaser",
134                Tags::Randomized => "randomized",
135                Tags::MonotonicQueue => "monotonic-queue",
136                Tags::MergeSort => "merge-sort",
137                Tags::Iterator => "iterator",
138                Tags::Concurrency => "concurrency",
139                Tags::DoublyLinkedList => "doubly-linked-list",
140                Tags::ProbabilityStatistics => "probability-statistics",
141                Tags::Quickselect => "quickselect",
142                Tags::BucketSort => "bucket-sort",
143                Tags::SuffixArray => "suffix-array",
144                Tags::MinimumSpanningTree => "minimum-spanning-tree",
145                Tags::CountingSort => "counting-sort",
146                Tags::Shell => "shell",
147                Tags::LineSweep => "line-sweep",
148                Tags::ReservoirSampling => "reservoir-sampling",
149                Tags::EulerianCircuit => "eulerian-circuit",
150                Tags::RadixSort => "radix-sort",
151                Tags::StronglyConnectedComponent => "strongly-connected-component",
152                Tags::RejectionSampling => "rejection-sampling",
153                Tags::BiconnectedComponent => "biconnected-component",
154            };
155
156            res_tags.push(String::from(tag));
157        }
158
159        self.filters.tags = res_tags;
160
161        self
162    }
163
164    pub async fn build(self) -> Result<ProblemData, Errors> {
165        let mut filters = json!({
166            "orderBy": self.filters.orderBy,
167            "sortOrder": self.filters.sortOrder,
168        });
169
170        if !self.filters.difficulty.is_empty() {
171            filters["difficulty"] = json!(self.filters.difficulty);
172        }
173
174        if !self.filters.status.is_empty() {
175            filters["status"] = json!(self.filters.status);
176        }
177
178        if !self.filters.tags.is_empty() {
179            filters["tags"] = json!(self.filters.tags);
180        }
181
182        if !self.key_word.is_empty() {
183            filters["searchKeywords"] = json!(self.key_word);
184        }
185
186        let query = json!({
187            "query": r#"
188                query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) {
189                    problemsetQuestionList: questionList(
190                        categorySlug: $categorySlug
191                        limit: $limit
192                        skip: $skip
193                        filters: $filters
194                    ) {
195                        total: totalNum
196                        questions: data {
197                            acRate
198                            difficulty
199                            freqBar
200                            frontendQuestionId: questionFrontendId
201                            isFavor
202                            paidOnly: isPaidOnly
203                            status
204                            title
205                            titleSlug
206                            topicTags {
207                                name
208                                id
209                                slug
210                            }
211                            hasSolution
212                            hasVideoSolution
213                        }
214                    }
215                }
216            "#,
217            "variables": {
218                "categorySlug": self.category,
219                "skip": 0,
220                "limit": self.limit,
221                "filters": filters
222            },
223            "operationName": "problemsetQuestionList"
224        });
225
226        let query = serde_json::to_string(&query)?;
227
228        let problem_info = self
229            .client
230            .get("https://leetcode.com/graphql/")
231            .body(query)
232            .send()
233            .await?
234            .text()
235            .await?;
236
237        Ok(serde_json::from_str::<ProblemData>(&problem_info)?)
238    }
239}
240
241#[derive(Debug)]
242pub enum Category {
243    AllTopics,
244    Algorithms,
245    DataBase,
246    JavaScript,
247    Shell,
248    Concurrency,
249}
250
251#[derive(Debug)]
252pub enum Difficulty {
253    Easy,
254    Medium,
255    Hard,
256}
257
258#[derive(Debug)]
259pub enum Status {
260    Todo,
261    Solved,
262    Attempted,
263}
264
265#[derive(Debug)]
266pub enum Tags {
267    Array,
268    String,
269    HashTable,
270    Math,
271    DynamicProgramming,
272    Sorting,
273    Greedy,
274    DepthFirstSearch,
275    Database,
276    BinarySearch,
277    BreadthFirstSearch,
278    Tree,
279    Matrix,
280    TwoPointers,
281    BinaryTree,
282    BitManipulation,
283    HeapPriorityQueue,
284    Stack,
285    Graph,
286    PrefixSum,
287    Simulation,
288    Design,
289    Counting,
290    Backtracking,
291    SlidingWindow,
292    UnionFind,
293    LinkedList,
294    OrderedSet,
295    MonotonicStack,
296    Enumeration,
297    Recursion,
298    Trie,
299    DivideAndConquer,
300    Bitmask,
301    BinarySearchTree,
302    NumberTheory,
303    Queue,
304    SegmentTree,
305    Memoization,
306    Geometry,
307    TopologicalSort,
308    BinaryIndexedTree,
309    HashFunction,
310    GameTheory,
311    ShortestPath,
312    Combinatorics,
313    DataStream,
314    Interactive,
315    StringMatching,
316    RollingHash,
317    Brainteaser,
318    Randomized,
319    MonotonicQueue,
320    MergeSort,
321    Iterator,
322    Concurrency,
323    DoublyLinkedList,
324    ProbabilityStatistics,
325    Quickselect,
326    BucketSort,
327    SuffixArray,
328    MinimumSpanningTree,
329    CountingSort,
330    Shell,
331    LineSweep,
332    ReservoirSampling,
333    EulerianCircuit,
334    RadixSort,
335    StronglyConnectedComponent,
336    RejectionSampling,
337    BiconnectedComponent,
338}