1pub use self::{
3 digit::Digit,
4 file::{code_path, load_script, test_cases_path},
5 filter::{filter, squash},
6 html::HTML,
7};
8
9mod digit {
11 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
47mod filter {
49 use crate::cache::models::Problem;
50 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 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
130mod html {
132 use crate::helper::{subscript, superscript};
133 use regex::Captures;
134 use scraper::Html;
135
136 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 frag.root_element()
158 .text()
159 .fold(String::new(), |acc, e| acc + e)
160 }
161 }
162}
163
164mod file {
165 pub fn suffix(l: &str) -> crate::Result<&'static str> {
167 match l {
168 "bash" => Ok("sh"),
169 "c" => Ok("c"),
170 "cpp" => Ok("cpp"),
171 "csharp" => Ok("cs"),
172 "elixir" => Ok("ex"),
173 "golang" => Ok("go"),
174 "java" => Ok("java"),
175 "javascript" => Ok("js"),
176 "kotlin" => Ok("kt"),
177 "mysql" => Ok("sql"),
178 "php" => Ok("php"),
179 "python" => Ok("py"),
180 "python3" => Ok("py"),
181 "ruby" => Ok("rb"),
182 "rust" => Ok("rs"),
183 "scala" => Ok("scala"),
184 "swift" => Ok("swift"),
185 "typescript" => Ok("ts"),
186 _ => Ok("c"),
187 }
188 }
189
190 use crate::{Error, cache::models::Problem};
191
192 pub fn test_cases_path(problem: &Problem) -> crate::Result<String> {
194 let conf = crate::config::Config::locate()?;
195 let mut path = format!("{}/{}.tests.dat", conf.storage.code()?, conf.code.pick);
196
197 path = path.replace("${fid}", &problem.fid.to_string());
198 path = path.replace("${slug}", &problem.slug.to_string());
199 Ok(path)
200 }
201
202 pub fn code_path(problem: &Problem, l: Option<String>) -> crate::Result<String> {
204 let conf = crate::config::Config::locate()?;
205 let mut lang = conf.code.lang;
206 if l.is_some() {
207 lang = l.ok_or(Error::NoneError)?;
208 }
209
210 let mut path = format!(
211 "{}/{}.{}",
212 conf.storage.code()?,
213 conf.code.pick,
214 suffix(&lang)?,
215 );
216
217 path = path.replace("${fid}", &problem.fid.to_string());
218 path = path.replace("${slug}", &problem.slug.to_string());
219
220 Ok(path)
221 }
222
223 pub fn load_script(module: &str) -> crate::Result<String> {
225 use std::fs::File;
226 use std::io::Read;
227 let conf = crate::config::Config::locate()?;
228 let mut script = "".to_string();
229 File::open(format!("{}/{}.py", conf.storage.scripts()?, module))?
230 .read_to_string(&mut script)?;
231
232 Ok(script)
233 }
234}