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