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
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 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 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 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 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}