leetcode_cli/
helper.rs

1//! A set of helper traits
2pub use self::{
3    digit::Digit,
4    file::{code_path, load_script, test_cases_path},
5    filter::{filter, squash},
6    html::HTML,
7};
8
9/// Convert i32 to specific digits string.
10mod digit {
11    /// Abstract Digit trait, fill the empty space to specific length.
12    pub trait Digit<T> {
13        fn digit(self, d: T) -> String;
14    }
15
16    impl Digit<i32> for i32 {
17        fn digit(self, d: i32) -> String {
18            let mut s = self.to_string();
19            let space = " ".repeat((d as usize) - s.len());
20            s.push_str(&space);
21
22            s
23        }
24    }
25
26    impl Digit<i32> for String {
27        fn digit(self, d: i32) -> String {
28            let mut s = self.clone();
29            let space = " ".repeat((d as usize) - self.len());
30            s.push_str(&space);
31
32            s
33        }
34    }
35
36    impl Digit<i32> for &'static str {
37        fn digit(self, d: i32) -> String {
38            let mut s = self.to_string();
39            let space = " ".repeat((d as usize) - self.len());
40            s.push_str(&space);
41
42            s
43        }
44    }
45}
46
47/// Question filter tool
48mod filter {
49    use crate::cache::models::Problem;
50    /// Abstract query filter
51    ///
52    /// ```sh
53    ///     -q, --query <query>          Filter questions by conditions:
54    ///                                  Uppercase means negative
55    ///                                  e = easy     E = m+h
56    ///                                  m = medium   M = e+h
57    ///                                  h = hard     H = e+m
58    ///                                  d = done     D = not done
59    ///                                  l = locked   L = not locked
60    ///                                  s = starred  S = not starred
61    /// ```
62    pub fn filter(ps: &mut Vec<Problem>, query: String) {
63        for p in query.chars() {
64            match p {
65                'l' => ps.retain(|x| x.locked),
66                'L' => ps.retain(|x| !x.locked),
67                's' => ps.retain(|x| x.starred),
68                'S' => ps.retain(|x| !x.starred),
69                'e' => ps.retain(|x| x.level == 1),
70                'E' => ps.retain(|x| x.level != 1),
71                'm' => ps.retain(|x| x.level == 2),
72                'M' => ps.retain(|x| x.level != 2),
73                'h' => ps.retain(|x| x.level == 3),
74                'H' => ps.retain(|x| x.level != 3),
75                'd' => ps.retain(|x| x.status == "ac"),
76                'D' => ps.retain(|x| x.status != "ac"),
77                _ => {}
78            }
79        }
80    }
81
82    /// Squash questions and ids
83    pub fn squash(ps: &mut Vec<Problem>, ids: Vec<String>) -> crate::Result<()> {
84        use std::collections::HashMap;
85
86        let mut map: HashMap<String, bool> = HashMap::new();
87        ids.iter().for_each(|x| {
88            map.insert(x.to_string(), true).unwrap_or_default();
89        });
90
91        ps.retain(|x| map.contains_key(&x.id.to_string()));
92        Ok(())
93    }
94}
95
96pub fn superscript(n: u8) -> String {
97    match n {
98        x if x >= 10 => format!("{}{}", superscript(n / 10), superscript(n % 10)),
99        0 => "⁰".to_string(),
100        1 => "¹".to_string(),
101        2 => "²".to_string(),
102        3 => "³".to_string(),
103        4 => "⁴".to_string(),
104        5 => "⁵".to_string(),
105        6 => "⁶".to_string(),
106        7 => "⁷".to_string(),
107        8 => "⁸".to_string(),
108        9 => "⁹".to_string(),
109        _ => n.to_string(),
110    }
111}
112
113pub fn subscript(n: u8) -> String {
114    match n {
115        x if x >= 10 => format!("{}{}", subscript(n / 10), subscript(n % 10)),
116        0 => "₀".to_string(),
117        1 => "₁".to_string(),
118        2 => "₂".to_string(),
119        3 => "₃".to_string(),
120        4 => "₄".to_string(),
121        5 => "₅".to_string(),
122        6 => "₆".to_string(),
123        7 => "₇".to_string(),
124        8 => "₈".to_string(),
125        9 => "₉".to_string(),
126        _ => n.to_string(),
127    }
128}
129
130/// Render html to command-line
131mod html {
132    use crate::helper::{subscript, superscript};
133    use regex::Captures;
134    use scraper::Html;
135
136    /// Html render plugin
137    pub trait HTML {
138        fn render(&self) -> String;
139    }
140
141    impl HTML for String {
142        fn render(&self) -> String {
143            let sup_re = regex::Regex::new(r"<sup>(?P<num>[0-9]*)</sup>").unwrap();
144            let sub_re = regex::Regex::new(r"<sub>(?P<num>[0-9]*)</sub>").unwrap();
145
146            let res = sup_re.replace_all(self, |cap: &Captures| {
147                let num: u8 = cap["num"].to_string().parse().unwrap();
148                superscript(num)
149            });
150
151            let res = sub_re.replace_all(&res, |cap: &Captures| {
152                let num: u8 = cap["num"].to_string().parse().unwrap();
153                subscript(num)
154            });
155
156            let frag = Html::parse_fragment(&res);
157
158            let res = frag
159                .root_element()
160                .text()
161                .fold(String::new(), |acc, e| acc + e);
162
163            res
164        }
165    }
166}
167
168mod file {
169    /// Convert file suffix from language type
170    pub fn suffix(l: &str) -> crate::Result<&'static str> {
171        match l {
172            "bash" => Ok("sh"),
173            "c" => Ok("c"),
174            "cpp" => Ok("cpp"),
175            "csharp" => Ok("cs"),
176            "elixir" => Ok("ex"),
177            "golang" => Ok("go"),
178            "java" => Ok("java"),
179            "javascript" => Ok("js"),
180            "kotlin" => Ok("kt"),
181            "mysql" => Ok("sql"),
182            "php" => Ok("php"),
183            "python" => Ok("py"),
184            "python3" => Ok("py"),
185            "ruby" => Ok("rb"),
186            "rust" => Ok("rs"),
187            "scala" => Ok("scala"),
188            "swift" => Ok("swift"),
189            "typescript" => Ok("ts"),
190            _ => Ok("c"),
191        }
192    }
193
194    use crate::{cache::models::Problem, Error};
195
196    /// Generate test cases path by fid
197    pub fn test_cases_path(problem: &Problem) -> crate::Result<String> {
198        let conf = crate::config::Config::locate()?;
199        let mut path = format!("{}/{}.tests.dat", conf.storage.code()?, conf.code.pick);
200
201        path = path.replace("${fid}", &problem.fid.to_string());
202        path = path.replace("${slug}", &problem.slug.to_string());
203        Ok(path)
204    }
205
206    /// Generate code path by fid
207    pub fn code_path(problem: &Problem, l: Option<String>) -> crate::Result<String> {
208        let conf = crate::config::Config::locate()?;
209        let mut lang = conf.code.lang;
210        if l.is_some() {
211            lang = l.ok_or(Error::NoneError)?;
212        }
213
214        let mut path = format!(
215            "{}/{}.{}",
216            conf.storage.code()?,
217            conf.code.pick,
218            suffix(&lang)?,
219        );
220
221        path = path.replace("${fid}", &problem.fid.to_string());
222        path = path.replace("${slug}", &problem.slug.to_string());
223
224        Ok(path)
225    }
226
227    /// Load python scripts
228    pub fn load_script(module: &str) -> crate::Result<String> {
229        use std::fs::File;
230        use std::io::Read;
231        let conf = crate::config::Config::locate()?;
232        let mut script = "".to_string();
233        File::open(format!("{}/{}.py", conf.storage.scripts()?, module))?
234            .read_to_string(&mut script)?;
235
236        Ok(script)
237    }
238}