1pub mod models;
3pub mod parser;
4pub mod schemas;
5mod sql;
6use self::models::*;
7use self::schemas::{problems::dsl::*, tags::dsl::*};
8use self::sql::*;
9use crate::helper::test_cases_path;
10use crate::{config::Config, err::Error, plugins::LeetCode};
11use anyhow::anyhow;
12use colored::Colorize;
13use diesel::prelude::*;
14use reqwest::Response;
15use serde::de::DeserializeOwned;
16use serde_json::Value;
17use std::collections::HashMap;
18
19pub fn conn(p: String) -> SqliteConnection {
21 SqliteConnection::establish(&p).unwrap_or_else(|_| panic!("Error connecting to {:?}", p))
22}
23
24#[derive(Clone, Debug)]
26#[derive(Default)]
27pub enum Run {
28 Test,
29 #[default]
30 Submit,
31}
32
33
34#[derive(Clone)]
36pub struct Cache(pub LeetCode);
37
38impl Cache {
39 fn conn(&self) -> Result<SqliteConnection, Error> {
41 Ok(conn(self.0.conf.storage.cache()?))
42 }
43
44 pub fn clean(&self) -> Result<(), Error> {
46 Ok(std::fs::remove_file(self.0.conf.storage.cache()?)?)
47 }
48
49 pub async fn update(self) -> Result<(), Error> {
51 self.download_problems().await?;
52 Ok(())
53 }
54
55 pub fn update_after_ac(self, rid: i32) -> Result<(), Error> {
56 let mut c = conn(self.0.conf.storage.cache()?);
57 let target = problems.filter(id.eq(rid));
58 diesel::update(target)
59 .set(status.eq("ac"))
60 .execute(&mut c)?;
61 Ok(())
62 }
63
64 async fn get_user_info(&self) -> Result<(String, bool), Error> {
65 let user = parser::user(self.clone().0.get_user_info().await?.json().await?);
66 match user {
67 None => Err(Error::NoneError),
68 Some(None) => Err(Error::CookieError),
69 Some(Some((s, b))) => Ok((s, b)),
70 }
71 }
72
73 async fn is_session_bad(&self) -> bool {
74 matches!(self.get_user_info().await, Err(Error::CookieError))
76 }
77
78 async fn resp_to_json<T: DeserializeOwned>(&self, resp: Response) -> Result<T, Error> {
79 let maybe_json: Result<T, _> = resp.json().await;
80 if maybe_json.is_err() && self.is_session_bad().await {
81 Err(Error::CookieError)
82 } else {
83 Ok(maybe_json?)
84 }
85 }
86
87 pub async fn download_problems(self) -> Result<Vec<Problem>, Error> {
89 info!("Fetching leetcode problems...");
90 let mut ps: Vec<Problem> = vec![];
91
92 for i in &self.0.conf.sys.categories.to_owned() {
93 let json = self
94 .0
95 .clone()
96 .get_category_problems(i)
97 .await?
98 .json() .await?;
100 parser::problem(&mut ps, json).ok_or(Error::NoneError)?;
101 }
102
103 diesel::replace_into(problems)
104 .values(&ps)
105 .execute(&mut self.conn()?)?;
106
107 Ok(ps)
108 }
109
110 pub fn get_problem(&self, rfid: i32) -> Result<Problem, Error> {
112 let p: Problem = problems.filter(fid.eq(rfid)).first(&mut self.conn()?)?;
113 if p.category != "algorithms" {
114 return Err(anyhow!("No support for database and shell questions yet").into());
115 }
116
117 Ok(p)
118 }
119
120 pub fn get_problem_id_from_name(&self, problem_name: &String) -> Result<i32, Error> {
122 let p: Problem = problems
123 .filter(name.eq(problem_name))
124 .first(&mut self.conn()?)?;
125 if p.category != "algorithms" {
126 return Err(anyhow!("No support for database and shell questions yet").into());
127 }
128 Ok(p.fid)
129 }
130
131 pub async fn get_daily_problem_id(&self) -> Result<i32, Error> {
133 parser::daily(
134 self.clone()
135 .0
136 .get_question_daily()
137 .await?
138 .json() .await?,
140 )
141 .ok_or(Error::NoneError)
142 }
143
144 pub fn get_problems(&self) -> Result<Vec<Problem>, Error> {
146 Ok(problems.load::<Problem>(&mut self.conn()?)?)
147 }
148
149 #[allow(clippy::useless_let_if_seq)]
151 pub async fn get_question(&self, rfid: i32) -> Result<Question, Error> {
152 let target: Problem = problems.filter(fid.eq(rfid)).first(&mut self.conn()?)?;
153
154 let ids = match target.level {
155 1 => target.fid.to_string().green(),
156 2 => target.fid.to_string().yellow(),
157 3 => target.fid.to_string().red(),
158 _ => target.fid.to_string().dimmed(),
159 };
160
161 println!(
162 "\n[{}] {} {}\n\n",
163 &ids,
164 &target.name.bold().underline(),
165 "is on the run...".dimmed()
166 );
167
168 if target.category != "algorithms" {
169 return Err(anyhow!("No support for database and shell questions yet").into());
170 }
171
172 let mut rdesc = Question::default();
173 if !target.desc.is_empty() {
174 rdesc = serde_json::from_str(&target.desc)?;
175 } else {
176 let json: Value = self
177 .0
178 .clone()
179 .get_question_detail(&target.slug)
180 .await?
181 .json()
182 .await?;
183 debug!("{:#?}", &json);
184 match parser::desc(&mut rdesc, json) {
185 None => return Err(Error::NoneError),
186 Some(false) => {
187 return if self.is_session_bad().await {
188 Err(Error::CookieError)
189 } else {
190 Err(Error::PremiumError)
191 }
192 }
193 Some(true) => (),
194 }
195
196 let sdesc = serde_json::to_string(&rdesc)?;
198 diesel::update(&target)
199 .set(desc.eq(sdesc))
200 .execute(&mut self.conn()?)?;
201 }
202
203 Ok(rdesc)
204 }
205
206 pub async fn get_tagged_questions(self, rslug: &str) -> Result<Vec<String>, Error> {
207 trace!("Geting {} questions...", &rslug);
208 let ids: Vec<String>;
209 let rtag = tags
210 .filter(tag.eq(rslug.to_string()))
211 .first::<Tag>(&mut self.conn()?);
212 if let Ok(t) = rtag {
213 trace!("Got {} questions from local cache...", &rslug);
214 ids = serde_json::from_str(&t.refs)?;
215 } else {
216 ids = parser::tags(
217 self.clone()
218 .0
219 .get_question_ids_by_tag(rslug)
220 .await?
221 .json()
222 .await?,
223 )
224 .ok_or(Error::NoneError)?;
225 let t = Tag {
226 r#tag: rslug.to_string(),
227 r#refs: serde_json::to_string(&ids)?,
228 };
229
230 diesel::insert_into(tags)
231 .values(&t)
232 .execute(&mut self.conn()?)?;
233 }
234
235 Ok(ids)
236 }
237
238 pub fn get_tags(&self) -> Result<Vec<Tag>, Error> {
239 Ok(tags.load::<Tag>(&mut self.conn()?)?)
240 }
241
242 async fn pre_run_code(
244 &self,
245 run: Run,
246 rfid: i32,
247 test_case: Option<String>,
248 ) -> Result<(HashMap<&'static str, String>, [String; 2]), Error> {
249 trace!("pre-run code...");
250 use crate::helper::code_path;
251 use std::fs::File;
252 use std::io::Read;
253
254 let mut p = self.get_problem(rfid)?;
255 if p.desc.is_empty() {
256 trace!("Problem description does not exist, pull desc and exec again...");
257 self.get_question(rfid).await?;
258 p = self.get_problem(rfid)?;
259 }
260
261 let d: Question = serde_json::from_str(&p.desc)?;
262 let conf = &self.0.conf;
263 let mut json: HashMap<&'static str, String> = HashMap::new();
264 let mut code: String = "".to_string();
265
266 let maybe_file_testcases: Option<String> = test_cases_path(&p)
267 .map(|file_name| {
268 let mut tests = "".to_string();
269 File::open(file_name)
270 .and_then(|mut file_descriptor| file_descriptor.read_to_string(&mut tests))
271 .map(|_| Some(tests))
272 .unwrap_or(None)
273 })
274 .unwrap_or(None);
275
276 let maybe_all_testcases: Option<String> = if d.all_cases.is_empty() {
277 None
278 } else {
279 Some(d.all_cases.to_string())
280 };
281
282 let test_case = test_case
288 .or(maybe_file_testcases)
289 .or(maybe_all_testcases)
290 .unwrap_or(d.case);
291
292 File::open(code_path(&p, None)?)?.read_to_string(&mut code)?;
293
294 let code = if conf.code.edit_code_marker {
295 let begin = code.find(&conf.code.start_marker);
296 let end = code.find(&conf.code.end_marker);
297 if let (Some(l), Some(r)) = (begin, end) {
298 code.get((l + conf.code.start_marker.len())..r)
299 .map(|s| s.to_string())
300 .unwrap_or_else(|| code)
301 } else {
302 code
303 }
304 } else {
305 code
306 };
307
308 json.insert("lang", conf.code.lang.to_string());
309 json.insert("question_id", p.id.to_string());
310 json.insert("typed_code", code);
311
312 json.insert("name", p.name.to_string());
314 json.insert("data_input", test_case);
315
316 let url = match run {
317 Run::Test => conf.sys.urls.test(&p.slug),
318 Run::Submit => {
319 json.insert("judge_type", "large".to_string());
320 conf.sys.urls.submit(&p.slug)
321 }
322 };
323
324 Ok((json, [url, conf.sys.urls.problem(&p.slug)]))
325 }
326
327 async fn recur_verify(&self, rid: String) -> Result<VerifyResult, Error> {
329 use std::time::Duration;
330
331 trace!("Run verify recursion...");
332 std::thread::sleep(Duration::from_micros(3000));
333
334 let json: VerifyResult = self
335 .resp_to_json(self.clone().0.verify_result(rid.clone()).await?)
336 .await?;
337
338 Ok(json)
339 }
340
341 pub async fn exec_problem(
343 &self,
344 rfid: i32,
345 run: Run,
346 test_case: Option<String>,
347 ) -> Result<VerifyResult, Error> {
348 trace!("Exec problem filter —— Test or Submit");
349 let (json, [url, refer]) = self.pre_run_code(run.clone(), rfid, test_case).await?;
350 trace!("Pre-run code result {:#?}, {}, {}", json, url, refer);
351
352 let text = self
353 .0
354 .clone()
355 .run_code(json.clone(), url.clone(), refer.clone())
356 .await?
357 .text()
358 .await?;
359
360 let run_res: RunCode = serde_json::from_str(&text).map_err(|e| {
361 anyhow!("JSON error: {e}, please double check your session and csrf config.")
362 })?;
363
364 trace!("Run code result {:#?}", run_res);
365
366 if match run {
368 Run::Test => run_res.interpret_id.is_empty(),
369 Run::Submit => run_res.submission_id == 0,
370 } {
371 return Err(Error::CookieError);
372 }
373
374 let mut res: VerifyResult = VerifyResult::default();
375 while res.state != "SUCCESS" {
376 res = match run {
377 Run::Test => self.recur_verify(run_res.interpret_id.clone()).await?,
378 Run::Submit => self.recur_verify(run_res.submission_id.to_string()).await?,
379 };
380 }
381 trace!("Recur verify result {:#?}", res);
382
383 res.name = json.get("name").ok_or(Error::NoneError)?.to_string();
384 res.data_input = json.get("data_input").ok_or(Error::NoneError)?.to_string();
385 res.result_type = run;
386 Ok(res)
387 }
388
389 pub fn new() -> Result<Self, Error> {
391 let conf = Config::locate()?;
392 let mut c = conn(conf.storage.cache()?);
393 diesel::sql_query(CREATE_PROBLEMS_IF_NOT_EXISTS).execute(&mut c)?;
394 diesel::sql_query(CREATE_TAGS_IF_NOT_EXISTS).execute(&mut c)?;
395
396 Ok(Cache(LeetCode::new()?))
397 }
398}