-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Tools): 🎉 Add basic quoj to mdoj command
- Loading branch information
Showing
8 changed files
with
400 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
"Backend", | ||
"Judger", | ||
"Testsuit", | ||
"Grpc" | ||
"Grpc", | ||
"Tools" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
[package] | ||
name = "tools" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[[bin]] | ||
name = "mdoj" | ||
path = "src/main.rs" | ||
|
||
[dependencies] | ||
anyhow = "1.0.86" | ||
serde_json = "1.0.127" | ||
zip = "2.2.0" | ||
|
||
|
||
[dependencies.tonic] | ||
workspace = true | ||
features = ["transport", "codegen", "prost", "channel"] | ||
|
||
[dependencies.grpc] | ||
path = "../grpc" | ||
features = ["backend", "client", "extra_trait", "transport"] | ||
|
||
[dependencies.serde] | ||
workspace = true | ||
features = ["derive"] | ||
|
||
[dependencies.tokio] | ||
workspace = true | ||
features = ["macros", "rt-multi-thread"] | ||
|
||
[dependencies.reqwest] | ||
version = "0.12.7" | ||
features = ["json"] | ||
|
||
[dependencies.clap] | ||
version = "4.5.16" | ||
features = ["derive"] | ||
|
||
[dependencies.futures] | ||
version = "0.3.30" | ||
default-features = false | ||
features = ["std"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
pub use grpc::backend::*; | ||
use tonic::{metadata::MetadataMap, IntoRequest, Request}; | ||
|
||
pub trait WithToken: Sized { | ||
/// this will add token to request. | ||
fn with_token(self, token: impl AsRef<str>) -> Request<Self>; | ||
} | ||
|
||
impl<T> WithToken for T | ||
where | ||
T: IntoRequest<T>, | ||
{ | ||
fn with_token(self, token: impl AsRef<str>) -> Request<Self> { | ||
let mut req = self.into_request(); | ||
let Ok(token) = token.as_ref().parse() else { | ||
return req; | ||
}; | ||
let mut metadata = MetadataMap::new(); | ||
metadata.insert("token", token); | ||
*req.metadata_mut() = metadata; | ||
req | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
mod grpc; | ||
mod quoj; | ||
mod quoj2mdoj; | ||
|
||
use anyhow::Result; | ||
use clap::Parser; | ||
|
||
#[derive(Debug, Parser)] | ||
enum Cli { | ||
Quoj2mdoj(quoj2mdoj::Quoj2mdoj), | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<()> { | ||
let cli = Cli::parse(); | ||
match cli { | ||
Cli::Quoj2mdoj(v) => quoj2mdoj::quoj2mdoj(v).await?, | ||
}; | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
pub mod problem; | ||
pub mod testcases; | ||
|
||
use anyhow::Result; | ||
use reqwest::{ | ||
header::{self, HeaderMap}, | ||
Client, IntoUrl, Url, | ||
}; | ||
use std::io::{Read, Seek}; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct QuojClient { | ||
base_url: Url, | ||
client: Client, | ||
} | ||
|
||
impl QuojClient { | ||
pub fn new(base_url: impl IntoUrl, session: String) -> Result<Self> { | ||
let base_url = base_url.into_url()?; | ||
|
||
let mut headers = HeaderMap::new(); | ||
headers.insert(header::COOKIE, format!("sessionid={session}").parse()?); | ||
|
||
let client = reqwest::ClientBuilder::new() | ||
.default_headers(headers) | ||
.build()?; | ||
|
||
Ok(Self { base_url, client }) | ||
} | ||
|
||
pub async fn problem(&self, id: usize) -> Result<problem::ProblemData> { | ||
problem::problem(&self.client, &self.base_url, id).await | ||
} | ||
|
||
pub async fn problems(&self) -> Result<Vec<problem::ProblemData>> { | ||
problem::problems(&self.client, &self.base_url).await | ||
} | ||
|
||
pub async fn testcases(&self, id: u64) -> Result<testcases::Testcases<impl Read + Seek>> { | ||
testcases::testcases(&self.client, &self.base_url, id).await | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
use anyhow::Result; | ||
use reqwest::{Client, Url}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct Problem { | ||
pub data: ProblemData, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct Problems { | ||
pub data: ProblemsData, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct ProblemsData { | ||
pub results: Vec<ProblemData>, | ||
pub total: u64, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct ProblemData { | ||
pub id: u64, | ||
pub tags: Vec<String>, | ||
pub title: String, | ||
pub description: String, | ||
pub input_description: String, | ||
pub output_description: String, | ||
pub samples: Vec<Sample>, | ||
pub test_case_id: String, | ||
pub test_case_score: Vec<TestCaseScore>, | ||
pub hint: String, | ||
pub languages: Vec<String>, | ||
pub create_time: String, | ||
pub last_update_time: Option<serde_json::Value>, | ||
pub time_limit: u64, | ||
pub memory_limit: u64, | ||
pub io_mode: IoMode, | ||
pub rule_type: String, | ||
pub difficulty: Difficulty, | ||
} | ||
|
||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)] | ||
pub enum Difficulty { | ||
Low, | ||
Mid, | ||
High, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct IoMode { | ||
pub input: String, | ||
pub output: String, | ||
pub io_mode: String, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct Sample { | ||
pub input: String, | ||
pub output: String, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct TestCaseScore { | ||
pub score: i64, | ||
pub input_name: String, | ||
pub output_name: String, | ||
} | ||
|
||
pub async fn problem(client: &Client, base_url: &Url, id: usize) -> Result<ProblemData> { | ||
let problem: Problem = client | ||
.get(base_url.join("admin/problem")?) | ||
.query(&[("id", id)]) | ||
.send() | ||
.await? | ||
.json() | ||
.await?; | ||
Ok(problem.data) | ||
} | ||
|
||
pub async fn problems(client: &Client, base_url: &Url) -> Result<Vec<ProblemData>> { | ||
const PAGE_SIZE: u64 = 250; | ||
let mut ret = vec![]; | ||
for i in 0.. { | ||
let problems: Problems = client | ||
.get(base_url.join("admin/problem")?) | ||
.query(&[("limit", PAGE_SIZE), ("offset", PAGE_SIZE * i)]) | ||
.send() | ||
.await? | ||
.json() | ||
.await?; | ||
|
||
ret.extend(problems.data.results); | ||
if problems.data.total <= PAGE_SIZE * i { | ||
break; | ||
} | ||
} | ||
Ok(ret) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
use std::io::{Cursor, Read, Seek}; | ||
|
||
use anyhow::Result; | ||
use reqwest::{Client, Url}; | ||
use zip::ZipArchive; | ||
|
||
pub async fn testcases( | ||
client: &Client, | ||
base_url: &Url, | ||
id: u64, | ||
) -> Result<Testcases<impl Read + Seek>> { | ||
let bytes = Cursor::new( | ||
client | ||
.get(base_url.join("admin/test_case")?) | ||
.query(&[("problem_id", id)]) | ||
.send() | ||
.await? | ||
.bytes() | ||
.await?, | ||
); | ||
let testcases = ZipArchive::new(bytes)?; | ||
Ok(Testcases(testcases)) | ||
} | ||
|
||
pub struct Testcases<T: Read + Seek>(ZipArchive<T>); | ||
|
||
impl<T: Read + Seek> Testcases<T> { | ||
pub fn testcase(&mut self, name: impl AsRef<str>) -> Result<Vec<u8>> { | ||
let mut buf = vec![]; | ||
let mut file = self.0.by_name(name.as_ref())?; | ||
buf.reserve_exact(file.size() as usize); | ||
file.read_to_end(&mut buf)?; | ||
Ok(buf) | ||
} | ||
} |
Oops, something went wrong.