algorithm_problem_client/codeforces/
client.rs

1use super::*;
2
3use crate::util;
4use crate::Result;
5use serde::Deserialize;
6
7const BASE_URL: &str = "https://codeforces.com/api";
8
9pub struct CodeforcesClient;
10
11impl Default for CodeforcesClient {
12    fn default() -> Self {
13        Self
14    }
15}
16
17impl CodeforcesClient {
18    pub async fn fetch_problems(&self) -> Result<Vec<CodeforcesProblem>> {
19        let url = format!("{}/problemset.problems", BASE_URL);
20        util::get_json::<CodeforcesProblemResponse>(&url)
21            .await
22            .map(|response| response.result.problems)
23    }
24
25    pub async fn fetch_submissions(
26        &self,
27        user_id: &str,
28        from: u32,
29        count: u32,
30    ) -> Result<Vec<CodeforcesSubmission>> {
31        let url = format!(
32            "{base_url}/user.status?handle={user_id}&from={from}&count={count}",
33            base_url = BASE_URL,
34            user_id = user_id,
35            from = from,
36            count = count,
37        );
38        util::get_json::<SubmissionResult>(&url)
39            .await
40            .map(|response| {
41                response
42                    .result
43                    .into_iter()
44                    .flat_map(|submission| submission.convert())
45                    .collect()
46            })
47    }
48}
49
50#[derive(Deserialize)]
51struct CodeforcesProblemResponse {
52    result: CodeforcesProblemResponseResult,
53}
54
55#[derive(Deserialize)]
56struct CodeforcesProblemResponseResult {
57    problems: Vec<CodeforcesProblem>,
58}
59
60#[derive(Deserialize)]
61struct SubmissionResult {
62    result: Vec<Submission>,
63}
64#[derive(Deserialize)]
65struct Submission {
66    id: u64,
67    problem: CodeforcesProblem,
68    author: Author,
69
70    #[serde(rename = "programmingLanguage")]
71    language: String,
72
73    verdict: String,
74}
75#[derive(Deserialize)]
76struct Author {
77    members: Vec<Member>,
78}
79#[derive(Deserialize)]
80struct Member {
81    handle: String,
82}
83
84impl Submission {
85    fn convert(self) -> Option<CodeforcesSubmission> {
86        let id = self.id;
87        let contest_id = self.problem.contest_id;
88        let problem_index = self.problem.index;
89        let verdict = self.verdict;
90        let language = self.language;
91        let user_id = self.author.members.into_iter().next()?.handle;
92        Some(CodeforcesSubmission {
93            id,
94            contest_id,
95            problem_index,
96            user_id,
97            verdict,
98            language,
99        })
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use futures::executor::block_on;
107
108    #[test]
109    fn test_fetch_problems() {
110        let client = CodeforcesClient::default();
111        assert!(block_on(client.fetch_problems()).unwrap().len() > 0);
112    }
113
114    #[test]
115    fn test_fetch_submissions() {
116        let client = CodeforcesClient::default();
117        let submissions = block_on(client.fetch_submissions("kenkoooo", 1, 10)).unwrap();
118        assert_eq!(submissions.len(), 10);
119    }
120}