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}