leetcode_api/render/
mod.rs

1pub mod qs_detail;
2pub mod run_res;
3pub mod submit_list;
4
5use std::{
6    io::prelude::Write,
7    process::{Command, Stdio},
8};
9
10#[cfg(feature = "ratatui")]
11use ratatui::text::Line;
12use regex::{Captures, Regex};
13
14#[derive(Clone, Copy)]
15#[derive(Debug)]
16#[derive(Default)]
17#[derive(PartialEq, Eq)]
18pub enum SupSub {
19    #[default]
20    Sup,
21    Sub,
22}
23
24#[derive(Clone, Copy)]
25#[derive(Debug)]
26#[derive(Default)]
27#[derive(PartialEq, Eq)]
28pub enum StTy {
29    Str,
30    #[default]
31    Tty,
32}
33
34pub trait Render {
35    /// uniform treatment `Question` detail to markdown String
36    ///
37    /// * `_with_env`: for `Question` whether display `Question` Compile Environment
38    fn to_md_str(&self, _with_env: bool) -> String {
39        String::new()
40    }
41
42    /// for ratatui's paragraph widget
43    #[cfg(feature = "ratatui")]
44    fn to_para_vec(&self) -> Vec<Line>;
45
46    /// use [`mdcat`](https://github.com/swsnr/mdcat/) render question content
47    fn render_with_mdcat(&self) {
48        let content = self.to_md_str(false);
49        'out: {
50            let Ok(mut child) = Command::new("mdcat")
51                .stdin(Stdio::piped())
52                .stdout(Stdio::inherit())
53                .spawn()
54            else {
55                break 'out;
56            };
57            if let Some(mut stdin) = child.stdin.take() {
58                if stdin
59                    .write_all(content.as_bytes())
60                    .is_err()
61                {
62                    break 'out;
63                };
64                // stdin drop here
65            }
66            else {
67                break 'out;
68            };
69
70            let Ok(exit_status) = child.wait()
71            else {
72                break 'out;
73            };
74            if exit_status.success() {
75                return;
76            }
77        }
78
79        println!("{content}");
80    }
81}
82
83pub fn to_sub_sup_script(content: &str) -> String {
84    let sup_re = Regex::new("<sup>(?P<num>[0-9]*)</sup>").expect("regex new failed");
85    let sub_re = Regex::new("<sub>(?P<num>[0-9]*)</sub>").expect("regex new failed");
86
87    let content = sup_re.replace_all(content, |cap: &Captures| {
88        let num = cap["num"].parse().unwrap_or_default();
89        superscript(num, SupSub::Sup)
90    });
91
92    let content = sub_re.replace_all(&content, |cap: &Captures| {
93        let num = cap["num"].parse().unwrap_or_default();
94        superscript(num, SupSub::Sub)
95    });
96
97    content.to_string()
98}
99
100const SUPER_NUM: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
101const SUB_NUM: [char; 10] = ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'];
102
103pub fn superscript(n: usize, sub_or_sup: SupSub) -> String {
104    let sub_or_sup = match sub_or_sup {
105        SupSub::Sup => SUPER_NUM,
106        SupSub::Sub => SUB_NUM,
107    };
108    match n {
109        0..=9 => sub_or_sup[n].to_string(),
110        mut num => {
111            // 2 is enough, avoid frequently alloc
112            let mut res = String::with_capacity(2);
113            while num > 0 {
114                res.push(sub_or_sup[num % 10]);
115                num /= 10;
116            }
117            res.chars().rev().collect()
118        },
119    }
120}