leetcode_cli/cache/
models.rs

1//! Leetcode data models
2use super::schemas::{problems, tags};
3use crate::helper::HTML;
4use colored::Colorize;
5use serde::{Deserialize, Serialize};
6use serde_json::Number;
7
8/// Tag model
9#[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/// Problem model
17#[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/// desc model
136#[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::*;
166/// deps of Question
167mod question {
168    use serde::{Deserialize, Serialize};
169
170    /// Code samples
171    #[derive(Debug, Default, Serialize, Deserialize)]
172    pub struct CodeDefintion(pub Vec<CodeDefintionInner>);
173
174    /// CodeDefinition Inner struct
175    #[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    /// Question status
184    #[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    /// Algorithm metadata
199    #[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    /// MetaData nested fields
207    #[derive(Debug, Default, Serialize, Deserialize)]
208    pub struct Param {
209        pub name: String,
210        pub r#type: String,
211    }
212
213    /// MetaData nested fields
214    #[derive(Debug, Default, Serialize, Deserialize)]
215    pub struct Return {
216        pub r#type: String,
217    }
218}
219
220/// run_code Result
221#[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/// verify result model
235#[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)]
245    // lang: String,
246    #[serde(default)]
247    pretty_lang: String,
248    // #[serde(default)]
249    // submission_id: String,
250    // #[serde(default)]
251    // run_success: bool,
252    #[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    // flatten
264    // #[serde(flatten, default)]
265    // info: VerifyInfo,
266    #[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                    // Pass Tests
296                    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                    // only Submit execute this branch
313                    // Submit Successfully
314                    // TODO: result should be all 1;
315                    // Lines below are sucks...
316                    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                    // prints
327                    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                    // Wrong Answer during testing
377                    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            // Failed some tests during submission
393            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            // Memory Exceeded
422            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            // Output Timeout Exceeded
430            //
431            // TODO: 13 and 14 might have some different,
432            // if anybody reach this, welcome to fix this!
433            13 | 14 => write!(f, "\n{}\n", &self.status.status_msg.yellow().bold(),)?,
434            // Runtime error
435            15 => write!(
436                f,
437                "\n{}\n{}\n'",
438                &self.status.status_msg.red().bold(),
439                &self.status.runtime_error
440            )?,
441            // Compile Error
442            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)]
512    // pub struct VerifyInfo {
513    //     #[serde(default)]
514    //     memory: i64,
515    //     #[serde(default)]
516    //     elapsed_time: i64,
517    //     #[serde(default)]
518    //     task_finish_time: i64,
519    // }
520
521    #[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)]
550        // compile_error: String,
551        #[serde(default)]
552        pub full_compile_error: String,
553    }
554
555    #[derive(Debug, Default, Deserialize)]
556    pub struct Expected {
557        // #[serde(default)]
558        // expected_status_code: i32,
559        // #[serde(default)]
560        // expected_lang: String,
561        // #[serde(default)]
562        // expected_run_success: bool,
563        // #[serde(default)]
564        // expected_status_runtime: String,
565        // #[serde(default)]
566        // expected_memory: i64,
567        // #[serde(default, deserialize_with = "ssr")]
568        // expected_code_output: Vec<String>,
569        // #[serde(default)]
570        // expected_elapsed_time: i64,
571        // #[serde(default)]
572        // expected_task_finish_time: i64,
573        #[serde(default, deserialize_with = "ssr")]
574        pub expected_code_answer: Vec<String>,
575    }
576}
577
578/// Formatter for str
579trait 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}