1use super::schemas::{problems, tags};
3use crate::helper::HTML;
4use colored::Colorize;
5use serde::{Deserialize, Serialize};
6use serde_json::Number;
7
8#[derive(Clone, Insertable, Queryable, Serialize, Debug)]
10#[diesel(table_name = tags)]
11pub struct Tag {
12 pub tag: String,
13 pub refs: String,
14}
15
16#[derive(AsChangeset, Clone, Identifiable, Insertable, Queryable, Serialize, Debug)]
18#[diesel(table_name = problems)]
19pub struct Problem {
20 pub category: String,
21 pub fid: i32,
22 pub id: i32,
23 pub level: i32,
24 pub locked: bool,
25 pub name: String,
26 pub percent: f32,
27 pub slug: String,
28 pub starred: bool,
29 pub status: String,
30 pub desc: String,
31}
32
33impl Problem {
34 fn display_level(&self) -> &str {
35 match self.level {
36 1 => "Easy",
37 2 => "Medium",
38 3 => "Hard",
39 _ => "Unknown",
40 }
41 }
42
43 pub fn desc_comment(&self, conf: &Config) -> String {
44 let mut res = String::new();
45 let comment_leading = &conf.code.comment_leading;
46 res += format!("{} Category: {}\n", comment_leading, self.category).as_str();
47 res += format!("{} Level: {}\n", comment_leading, self.display_level(),).as_str();
48 res += format!("{} Percent: {}%\n\n", comment_leading, self.percent).as_str();
49
50 res + "\n"
51 }
52}
53
54static DONE: &str = " ✔";
55static ETC: &str = "...";
56static LOCK: &str = "🔒";
57static NDONE: &str = "✘";
58static SPACE: &str = " ";
59impl std::fmt::Display for Problem {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 let space_2 = SPACE.repeat(2);
62 let mut lock = space_2.as_str();
63 let mut done = space_2.normal();
64 let mut id = "".to_string();
65 let mut name = "".to_string();
66 let mut level = "".normal();
67
68 if self.locked {
69 lock = LOCK
70 };
71 if self.status == "ac" {
72 done = DONE.green().bold();
73 } else if self.status == "notac" {
74 done = NDONE.green().bold();
75 }
76
77 match self.fid.to_string().len() {
78 1 => {
79 id.push_str(&SPACE.repeat(2));
80 id.push_str(&self.fid.to_string());
81 id.push_str(SPACE);
82 }
83 2 => {
84 id.push_str(SPACE);
85 id.push_str(&self.fid.to_string());
86 id.push_str(SPACE);
87 }
88 3 => {
89 id.push_str(SPACE);
90 id.push_str(&self.fid.to_string());
91 }
92 4 => {
93 id.push_str(&self.fid.to_string());
94 }
95 _ => {
96 id.push_str(&space_2);
97 id.push_str(&space_2);
98 }
99 }
100
101 if self.name.len() < 60_usize {
102 name.push_str(&self.name);
103 name.push_str(&SPACE.repeat(60 - &self.name.len()));
104 } else {
105 name.push_str(&self.name[..49]);
106 name = name.trim_end().to_string();
107 name.push_str(ETC);
108 name.push_str(&SPACE.repeat(60 - name.len()));
109 }
110
111 level = match self.level {
112 1 => "Easy ".bright_green(),
113 2 => "Medium".bright_yellow(),
114 3 => "Hard ".bright_red(),
115 _ => level,
116 };
117
118 let mut pct = self.percent.to_string();
119 if pct.len() < 5 {
120 pct.push_str(&"0".repeat(5 - pct.len()));
121 }
122 write!(
123 f,
124 " {} {} [{}] {} {} ({} %)",
125 lock,
126 done,
127 id,
128 name,
129 level,
130 &pct[..5]
131 )
132 }
133}
134
135#[derive(Debug, Default, Serialize, Deserialize)]
137pub struct Question {
138 pub content: String,
139 pub stats: Stats,
140 pub defs: CodeDefintion,
141 pub case: String,
142 pub all_cases: String,
143 pub metadata: MetaData,
144 pub test: bool,
145 pub t_content: String,
146}
147
148impl Question {
149 pub fn desc(&self) -> String {
150 self.content.render()
151 }
152
153 pub fn desc_comment(&self, conf: &Config) -> String {
154 let desc = self.content.render();
155
156 let mut res = desc.lines().fold("\n".to_string(), |acc, e| {
157 acc + "" + conf.code.comment_leading.as_str() + " " + e + "\n"
158 });
159 res += " \n";
160
161 res
162 }
163}
164
165use question::*;
166mod question {
168 use serde::{Deserialize, Serialize};
169
170 #[derive(Debug, Default, Serialize, Deserialize)]
172 pub struct CodeDefintion(pub Vec<CodeDefintionInner>);
173
174 #[derive(Debug, Default, Serialize, Deserialize)]
176 pub struct CodeDefintionInner {
177 pub value: String,
178 pub text: String,
179 #[serde(alias = "defaultCode")]
180 pub code: String,
181 }
182
183 #[derive(Debug, Default, Serialize, Deserialize)]
185 pub struct Stats {
186 #[serde(alias = "totalAccepted")]
187 tac: String,
188 #[serde(alias = "totalSubmission")]
189 tsm: String,
190 #[serde(alias = "totalAcceptedRaw")]
191 tacr: i32,
192 #[serde(alias = "totalSubmissionRaw")]
193 tsmr: i32,
194 #[serde(alias = "acRate")]
195 rate: String,
196 }
197
198 #[derive(Debug, Default, Serialize, Deserialize)]
200 pub struct MetaData {
201 pub name: Option<String>,
202 pub params: Option<Vec<Param>>,
203 pub r#return: Return,
204 }
205
206 #[derive(Debug, Default, Serialize, Deserialize)]
208 pub struct Param {
209 pub name: String,
210 pub r#type: String,
211 }
212
213 #[derive(Debug, Default, Serialize, Deserialize)]
215 pub struct Return {
216 pub r#type: String,
217 }
218}
219
220#[derive(Debug, Deserialize)]
222pub struct RunCode {
223 #[serde(default)]
224 pub interpret_id: String,
225 #[serde(default)]
226 pub test_case: String,
227 #[serde(default)]
228 pub submission_id: i64,
229}
230
231use super::parser::ssr;
232use crate::cache::Run;
233
234#[derive(Default, Debug, Deserialize)]
236pub struct VerifyResult {
237 pub state: String,
238 #[serde(skip)]
239 pub name: String,
240 #[serde(skip)]
241 pub data_input: String,
242 #[serde(skip)]
243 pub result_type: Run,
244 #[serde(default)]
247 pretty_lang: String,
248 #[serde(default)]
253 correct_answer: bool,
254 #[serde(default, deserialize_with = "ssr")]
255 code_answer: Vec<String>,
256 #[serde(default, deserialize_with = "ssr")]
257 code_output: Vec<String>,
258 #[serde(default, deserialize_with = "ssr")]
259 expected_output: Vec<String>,
260 #[serde(default, deserialize_with = "ssr")]
261 std_output: Vec<String>,
262
263 #[serde(flatten, default)]
267 status: VerifyStatus,
268 #[serde(flatten, default)]
269 analyse: Analyse,
270 #[serde(flatten, default)]
271 expected: Expected,
272 #[serde(flatten, default)]
273 error: CompileError,
274 #[serde(flatten, default)]
275 submit: Submit,
276}
277
278impl std::fmt::Display for VerifyResult {
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 let ca = match &self.code_answer.len() {
281 1 => self.code_answer[0].to_string(),
282 _ => self.code_answer.join("↩ "),
283 };
284
285 let eca = match &self.expected.expected_code_answer.len() {
286 1 => self.expected.expected_code_answer[0].to_string(),
287 _ => self.expected.expected_code_answer.join("↩ "),
288 };
289
290 debug!("{:#?}", &self);
291
292 match &self.status.status_code {
293 10 => {
294 if matches!(self.result_type, Run::Test) && self.correct_answer {
295 write!(
297 f,
298 "\n{}{}{}\n{}{}{}{}{}{}\n",
299 &self.status.status_msg.green().bold(),
300 &"Runtime: ".before_spaces(7).dimmed(),
301 &self.status.status_runtime.dimmed(),
302 &"\nYour input:".after_spaces(4),
303 &self.data_input.replace('\n', "↩ "),
304 &"\nOutput:".after_spaces(8),
305 ca,
306 &"\nExpected:".after_spaces(6),
307 eca,
308 )?
309 } else if matches!(self.result_type, Run::Submit)
310 && !self.submit.compare_result.is_empty()
311 {
312 let cache = super::Cache::new().expect("cache gen failed");
317 cache
318 .update_after_ac(
319 self.submit
320 .question_id
321 .parse()
322 .expect("submit successfully, parse question_id to i32 failed"),
323 )
324 .expect("update ac to cache failed");
325
326 let rp = if let Some(n) = &self.analyse.runtime_percentile {
328 if n.is_f64() {
329 n.as_f64().unwrap_or(0.0) as i64
330 } else {
331 n.as_i64().unwrap_or(0)
332 }
333 } else {
334 0
335 };
336
337 let mp = if let Some(n) = &self.analyse.memory_percentile {
338 if n.is_f64() {
339 n.as_f64().unwrap_or(0.0) as i64
340 } else {
341 n.as_i64().unwrap_or(0)
342 }
343 } else {
344 0
345 };
346
347 write!(
348 f,
349 "\n{}{}{}\
350 , faster than \
351 {}{}\
352 of \
353 {} \
354 online submissions for \
355 {}.\n\n\
356 {}{}\
357 , less than \
358 {}{}\
359 of \
360 {} {}.\n\n",
361 "Success\n\n".green().bold(),
362 "Runtime: ".dimmed(),
363 &self.status.status_runtime.bold(),
364 rp.to_string().bold(),
365 "% ".bold(),
366 &self.pretty_lang,
367 &self.name,
368 "Memory Usage: ".dimmed(),
369 &self.status.status_memory.bold(),
370 mp.to_string().bold(),
371 "% ".bold(),
372 &self.pretty_lang,
373 &self.name,
374 )?
375 } else {
376 write!(
378 f,
379 "\n{}{}{}\n{}{}{}{}{}{}\n",
380 "Wrong Answer".red().bold(),
381 " Runtime: ".dimmed(),
382 &self.status.status_runtime.dimmed(),
383 &"\nYour input:".after_spaces(4),
384 &self.data_input.replace('\n', "↩ "),
385 &"\nOutput:".after_spaces(8),
386 ca,
387 &"\nExpected:".after_spaces(6),
388 eca,
389 )?
390 }
391 }
392 11 => write!(
394 f,
395 "\n{}\n\n{}{}\n{}{}\n{}{}{}{}{}{}\n",
396 &self.status.status_msg.red().bold(),
397 "Cases passed:".after_spaces(2).green(),
398 &self
399 .analyse
400 .total_correct
401 .as_ref()
402 .unwrap_or(&Number::from(0))
403 .to_string()
404 .green(),
405 &"Total cases:".after_spaces(3).yellow(),
406 &self
407 .analyse
408 .total_testcases
409 .as_ref()
410 .unwrap_or(&Number::from(0))
411 .to_string()
412 .bold()
413 .yellow(),
414 &"Last case:".after_spaces(5).dimmed(),
415 &self.submit.last_testcase.replace('\n', "↩ ").dimmed(),
416 &"\nOutput:".after_spaces(8),
417 self.code_output[0],
418 &"\nExpected:".after_spaces(6),
419 self.expected_output[0],
420 )?,
421 12 => write!(
423 f,
424 "\n{}\n\n{}{}\n",
425 &self.status.status_msg.yellow().bold(),
426 &"Last case:".after_spaces(5).dimmed(),
427 &self.data_input.replace('\n', "↩ "),
428 )?,
429 13 | 14 => write!(f, "\n{}\n", &self.status.status_msg.yellow().bold(),)?,
434 15 => write!(
436 f,
437 "\n{}\n{}\n'",
438 &self.status.status_msg.red().bold(),
439 &self.status.runtime_error
440 )?,
441 20 => write!(
443 f,
444 "\n{}:\n\n{}\n",
445 &self.status.status_msg.red().bold(),
446 &self.error.full_compile_error.dimmed()
447 )?,
448 _ => write!(
449 f,
450 "{}{}{}{}{}{}{}{}",
451 "\nUnknown Error...\n".red().bold(),
452 "\nBingo! Welcome to fix this! Pull your request at ".yellow(),
453 "https://github.com/clearloop/leetcode-cli/pulls"
454 .dimmed()
455 .underline(),
456 ", and this file is located at ".yellow(),
457 "leetcode-cli/src/cache/models.rs".dimmed().underline(),
458 " waiting for you! Yep, line ".yellow(),
459 "385".dimmed().underline(),
460 ".\n".yellow(),
461 )?,
462 };
463
464 match &self.result_type {
465 Run::Test => {
466 if !self.code_output.is_empty() {
467 write!(
468 f,
469 "{}{}",
470 &"Stdout:".after_spaces(8).purple(),
471 &self.code_output.join(&"\n".after_spaces(15))
472 )
473 } else {
474 write!(f, "")
475 }
476 }
477 _ => {
478 if !self.std_output.is_empty() {
479 write!(
480 f,
481 "{}{}",
482 &"Stdout:".after_spaces(8).purple(),
483 &self.std_output[0].replace('\n', &"\n".after_spaces(15))
484 )
485 } else {
486 write!(f, "")
487 }
488 }
489 }
490 }
491}
492
493use crate::Config;
494use verify::*;
495
496mod verify {
497 use super::super::parser::ssr;
498 use serde::Deserialize;
499 use serde_json::Number;
500
501 #[derive(Debug, Default, Deserialize)]
502 pub struct Submit {
503 #[serde(default)]
504 pub question_id: String,
505 #[serde(default)]
506 pub last_testcase: String,
507 #[serde(default)]
508 pub compare_result: String,
509 }
510
511 #[derive(Debug, Default, Deserialize)]
522 pub struct Analyse {
523 #[serde(default)]
524 pub total_correct: Option<Number>,
525 #[serde(default)]
526 pub total_testcases: Option<Number>,
527 #[serde(default)]
528 pub runtime_percentile: Option<Number>,
529 #[serde(default)]
530 pub memory_percentile: Option<Number>,
531 }
532
533 #[derive(Debug, Default, Deserialize)]
534 pub struct VerifyStatus {
535 #[serde(default)]
536 pub status_code: i32,
537 #[serde(default)]
538 pub status_msg: String,
539 #[serde(default)]
540 pub status_memory: String,
541 #[serde(default)]
542 pub status_runtime: String,
543 #[serde(default)]
544 pub runtime_error: String,
545 }
546
547 #[derive(Debug, Default, Deserialize)]
548 pub struct CompileError {
549 #[serde(default)]
552 pub full_compile_error: String,
553 }
554
555 #[derive(Debug, Default, Deserialize)]
556 pub struct Expected {
557 #[serde(default, deserialize_with = "ssr")]
574 pub expected_code_answer: Vec<String>,
575 }
576}
577
578trait Formatter {
580 fn after_spaces(&self, spaces: usize) -> String;
581 fn before_spaces(&self, spaces: usize) -> String;
582}
583
584impl Formatter for str {
585 fn after_spaces(&self, spaces: usize) -> String {
586 let mut r = String::new();
587 r.push_str(self);
588 r.push_str(&" ".repeat(spaces));
589 r
590 }
591
592 fn before_spaces(&self, spaces: usize) -> String {
593 let mut r = String::new();
594 r.push_str(&" ".repeat(spaces));
595 r.push_str(self);
596 r
597 }
598}