leetcode_api/leetcode/impl_lc/
judge.rs

1use std::time::Duration;
2
3use lcode_config::global::G_USER_CONFIG;
4use miette::Result;
5use regex::Regex;
6use tokio::{join, time::sleep};
7use tracing::{debug, trace};
8
9use crate::{
10    Json,
11    dao::{query::Query, save_info::FileInfo},
12    leetcode::{
13        IdSlug, LeetCode,
14        graphqls::GraphqlQuery,
15        resps::{
16            run_res::*,
17            submit_list::{SubmissionData, SubmissionList},
18        },
19    },
20};
21
22impl LeetCode {
23    pub async fn add_test_case(&self, id: u32, case: &str) -> Result<()> {
24        if case.is_empty() {
25            return Ok(());
26        }
27        let idx = Query::get_question_index(&IdSlug::Id(id)).await?;
28
29        let info = FileInfo::build(&idx).await?;
30        info.append_test_case(case).await?;
31
32        Ok(())
33    }
34
35    pub async fn reset_test_case(&self, id: u32) -> Result<()> {
36        let idx = Query::get_question_index(&IdSlug::Id(id)).await?;
37        let detail = self
38            .get_qs_detail(IdSlug::Id(id), false, false)
39            .await?;
40        let info = FileInfo::build(&idx).await?;
41        info.reset_test_case(&detail.example_testcases)
42            .await?;
43
44        Ok(())
45    }
46}
47
48impl LeetCode {
49    /// submit code by id or slug, once submit one question
50    ///
51    /// * `idslug`: id or slug
52    pub async fn submit_code(&self, idslug: IdSlug) -> Result<(SubmitInfo, RunResult)> {
53        let (code, pb) = join!(
54            self.get_user_code(idslug.clone()),
55            Query::get_question_index(&idslug)
56        );
57        let ((code, _), pb) = (code?, pb?);
58
59        let mut json: Json = Json::new();
60        json.insert("lang", G_USER_CONFIG.config.lang.clone());
61        json.insert("question_id", pb.question_id.to_string());
62        json.insert("typed_code", code);
63
64        trace!("submit insert json: {:#?}", json);
65
66        let sub_info: SubmitInfo = match self
67            .request(
68                &G_USER_CONFIG
69                    .urls
70                    .mod_submit(&pb.question_title_slug),
71                Some(&json),
72                self.headers.clone(),
73            )
74            .await
75        {
76            Ok(it) => it,
77            Err(err) => {
78                return Ok((
79                    SubmitInfo::default(),
80                    RunResultBuild::default()
81                        .set_status_msg(err.to_string())
82                        .build(),
83                ));
84            },
85        };
86
87        let last_sub_result = self.get_submit_res(&sub_info).await?;
88        debug!("last submit result: {:#?}", last_sub_result);
89
90        Ok((sub_info, last_sub_result))
91    }
92
93    /// Get one submit info
94    ///
95    /// * `sub_id`: be fetch `submission_id`
96    pub async fn get_submit_res(&self, sub_id: &SubmitInfo) -> Result<RunResult> {
97        let test_res_url = G_USER_CONFIG
98            .urls
99            .mod_submissions(&sub_id.submission_id().to_string());
100        trace!("start get last submit detail");
101
102        for _ in 0..9 {
103            sleep(Duration::from_millis(700)).await;
104
105            let resp_json: RunResult = self
106                .request(&test_res_url, None, self.headers.clone())
107                .await?;
108            if resp_json.success() {
109                return Ok(resp_json);
110            }
111        }
112        Ok(RunResultBuild::default()
113            .set_status_msg(
114                "Get the submit result error, please check your code, it may fail to execute, or \
115                 check your network"
116                    .to_owned(),
117            )
118            .build())
119    }
120
121    /// Get all submission results for a question
122    pub async fn all_submit_res(&self, idslug: IdSlug) -> Result<SubmissionList> {
123        let pb = Query::get_question_index(&idslug).await?;
124
125        let json: Json = GraphqlQuery::subission_list(&pb.question_title_slug);
126
127        let pat: SubmissionData = self
128            .request(
129                &G_USER_CONFIG.urls.graphql,
130                Some(&json),
131                self.headers.clone(),
132            )
133            .await?;
134
135        Ok(pat.submission_list())
136    }
137
138    pub async fn test_code(&self, idslug: IdSlug) -> Result<(TestInfo, RunResult)> {
139        let (code, pb) = join!(
140            self.get_user_code(idslug.clone()),
141            Query::get_question_index(&idslug)
142        );
143        let ((code, test_case), pb) = (code?, pb?);
144        debug!("code:\n{}", code);
145
146        let mut json: Json = Json::new();
147        json.insert("lang", G_USER_CONFIG.config.lang.clone());
148        json.insert("question_id", pb.question_id.to_string());
149        json.insert("typed_code", code);
150        json.insert("data_input", test_case);
151
152        let test_info: TestInfo = match self
153            .request(
154                &G_USER_CONFIG
155                    .urls
156                    .mod_test(&pb.question_title_slug),
157                Some(&json),
158                self.headers.clone(),
159            )
160            .await
161        {
162            Ok(it) => it,
163            Err(err) => {
164                return Ok((
165                    TestInfo::default(),
166                    RunResultBuild::default()
167                        .set_status_msg(err.to_string())
168                        .build(),
169                ));
170            },
171        };
172
173        let test_result = self.get_test_res(&test_info).await?;
174
175        Ok((test_info, test_result))
176    }
177
178    /// Get the last submission results for a question
179    async fn get_test_res(&self, test_info: &TestInfo) -> Result<RunResult> {
180        for _ in 0..9 {
181            sleep(Duration::from_millis(700)).await;
182
183            let resp_json: RunResult = self
184                .request(
185                    &G_USER_CONFIG
186                        .urls
187                        .mod_submissions(test_info.interpret_id()),
188                    None,
189                    self.headers.clone(),
190                )
191                .await?;
192            if resp_json.success() {
193                return Ok(resp_json);
194            }
195        }
196        Ok(RunResultBuild::default()
197            .set_status_msg(
198                "Get the test result error, please check your network,or check test case it may \
199                 not correct"
200                    .to_owned(),
201            )
202            .build())
203    }
204
205    /// Get user code as string(`code`, `test case`)
206    pub async fn get_user_code(&self, idslug: IdSlug) -> Result<(String, String)> {
207        let pb = Query::get_question_index(&idslug).await?;
208        let chf = FileInfo::build(&pb).await?;
209        let (code, mut test_case) = chf.get_user_code(&idslug).await?;
210
211        if test_case.is_empty() {
212            test_case = self
213                .get_qs_detail(idslug, false, true)
214                .await?
215                .example_testcases;
216        }
217        let (start, end, ..) = G_USER_CONFIG.get_lang_info();
218        let code_re = Regex::new(&format!(r"(?s){}\n(?P<code>.*){}", start, end))
219            .expect("get_user_code regex new failed");
220
221        // sep code just get needed
222        #[expect(clippy::option_if_let_else, reason = "borrow checker")]
223        let res = match code_re.captures(&code) {
224            Some(val) => val["code"].to_owned(),
225            None => code,
226        };
227
228        Ok((res, test_case))
229    }
230}