Skip to content

Commit 0481e5f

Browse files
authored
Merge pull request #123 from traP-jp/feat/#122-local-problem-registry
Feat/#122 local problem registry
2 parents 458f1bf + 4cdfdfd commit 0481e5f

File tree

12 files changed

+244
-13
lines changed

12 files changed

+244
-13
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ pyo3-stub-gen = "0.7.0"
2424
members = [
2525
"lib/jobapi",
2626
"lib/local_jobapi",
27+
"lib/local_problem_registry",
2728
"lib/grpc_schema",
2829
"lib/judge_core",
2930
"pylib/traopy",
30-
"app/judge_control_app",
31+
"app/judge_control_app", "lib/local_problem_registry",
3132
]
3233
resolver = "2"

lib/judge_core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ pub mod job;
33
pub mod problem_registry;
44
pub mod procedure;
55
pub mod runner;
6+
pub mod writer_schema_transpiler;

lib/judge_core/src/problem_registry.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::{identifiers::ResourceId, procedure::*};
2-
use anyhow::Result;
32
use futures::Future;
43

54
/// ProblemRegistryServer uploads contents of problems to the registry in webservice-backend server.
65
pub trait ProblemRegistryServer {
6+
// Memo: use crate::writer_schema_transpiler::transpile as the core logic
77
fn register(
88
&self,
99
problem: writer_schema::Procedure,
10-
) -> impl Future<Output = Result<registered::Procedure>>;
10+
) -> impl Future<Output = Result<registered::Procedure, RegistrationError>>;
1111
}
1212

1313
/// ProblemRegistryClient fetches contents of problems from the registry in judge server.
@@ -25,3 +25,11 @@ pub enum ResourceFetchError {
2525
#[error("Resource {0} not found")]
2626
NotFound(ResourceId),
2727
}
28+
29+
#[derive(Debug, Clone, thiserror::Error)]
30+
pub enum RegistrationError {
31+
#[error("Internal error while registering a problem: {0}")]
32+
InternalError(String),
33+
#[error("Invalid problem procedure schema: {0}")]
34+
InvalidSchema(String),
35+
}

lib/judge_core/src/procedure/registered.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub struct Procedure {
88
}
99

1010
pub struct RuntimeText {
11-
pub name: String,
11+
pub label: String,
1212
pub dep_id: DepId,
1313
}
1414

lib/judge_core/src/procedure/writer_schema.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use serde::{Deserialize, Serialize};
2-
use std::collections::HashMap;
32

43
#[derive(Debug, Clone, Serialize, Deserialize)]
54
pub struct Procedure {
6-
pub resources: HashMap<String, ResourceKind>,
7-
pub executions: HashMap<String, Execution>,
8-
pub scripts: HashMap<String, Script>,
5+
pub resources: Vec<ResourceKind>,
6+
pub executions: Vec<Execution>,
7+
pub scripts: Vec<Script>,
98
}
109

1110
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -36,6 +35,7 @@ pub enum ResourceKind {
3635
#[derive(Debug, Clone, Serialize, Deserialize)]
3736
pub struct RuntimeText {
3837
pub name: String,
38+
pub label: String,
3939
}
4040

4141
#[derive(Debug, Clone, Serialize, Deserialize)]
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#![allow(unused_variables)]
2+
3+
use crate::{
4+
problem_registry::*,
5+
procedure::{writer_schema::*, *},
6+
*,
7+
};
8+
use std::collections::HashMap;
9+
10+
pub fn transpile(
11+
problem: writer_schema::Procedure,
12+
) -> Result<
13+
(
14+
registered::Procedure,
15+
HashMap<identifiers::ResourceId, String>,
16+
),
17+
RegistrationError,
18+
> {
19+
let mut name_to_id = HashMap::new();
20+
for resource in problem.resources.iter() {
21+
let name = match resource {
22+
ResourceKind::TextFile(content) => content.name.clone(),
23+
ResourceKind::EmptyDirectory(empty_dir) => empty_dir.name.clone(),
24+
ResourceKind::RuntimeTextFile(runtime_text) => runtime_text.name.clone(),
25+
};
26+
let id = identifiers::DepId::new();
27+
name_to_id.insert(name.clone(), id);
28+
}
29+
for script in problem.scripts.iter() {
30+
let name = script.name.clone();
31+
let id = identifiers::DepId::new();
32+
name_to_id.insert(name.clone(), id);
33+
}
34+
for execution in problem.executions.iter() {
35+
let name = execution.name.clone();
36+
let id = identifiers::DepId::new();
37+
name_to_id.insert(name.clone(), id);
38+
}
39+
let mut content_to_id = HashMap::new();
40+
for resource in problem.resources.iter() {
41+
if let ResourceKind::TextFile(content) = resource {
42+
let id = identifiers::ResourceId::new();
43+
content_to_id.insert(content.content.clone(), id);
44+
}
45+
}
46+
let mut runtime_texts = Vec::new();
47+
let mut texts = Vec::new();
48+
let mut empty_directories = Vec::new();
49+
for resource in problem.resources.iter() {
50+
match resource {
51+
ResourceKind::TextFile(content) => {
52+
let dep_id = name_to_id
53+
.get(&content.name)
54+
.ok_or(RegistrationError::InvalidSchema(
55+
"TextFile name not found".to_string(),
56+
))?
57+
.clone();
58+
let text = registered::Text {
59+
resource_id: content_to_id
60+
.get(&content.content)
61+
.ok_or(RegistrationError::InvalidSchema(
62+
"TextFile content not found".to_string(),
63+
))?
64+
.clone(),
65+
dep_id: dep_id.clone(),
66+
};
67+
texts.push(text);
68+
}
69+
ResourceKind::EmptyDirectory(empty_dir) => {
70+
let dep_id = name_to_id
71+
.get(&empty_dir.name)
72+
.ok_or(RegistrationError::InvalidSchema(
73+
"EmptyDirectory name not found".to_string(),
74+
))?
75+
.clone();
76+
let empty_directory = registered::EmptyDirectory {
77+
dep_id: dep_id.clone(),
78+
};
79+
empty_directories.push(empty_directory);
80+
}
81+
ResourceKind::RuntimeTextFile(runtime_text) => {
82+
let dep_id = name_to_id
83+
.get(&runtime_text.name)
84+
.ok_or(RegistrationError::InvalidSchema(
85+
"RuntimeText name not found".to_string(),
86+
))?
87+
.clone();
88+
let runtime_text = registered::RuntimeText {
89+
label: runtime_text.label.clone(),
90+
dep_id: dep_id.clone(),
91+
};
92+
runtime_texts.push(runtime_text);
93+
}
94+
}
95+
}
96+
let mut executions = Vec::new();
97+
for execution in problem.executions.iter() {
98+
let script = execution.script_name.clone();
99+
let mut dependencies = Vec::new();
100+
for dep in execution.depends_on.iter() {
101+
let dep_id = name_to_id
102+
.get(&dep.ref_to)
103+
.ok_or(RegistrationError::InvalidSchema(
104+
"Dependency name not found".to_string(),
105+
))?
106+
.clone();
107+
let depends_on = registered::DependsOn {
108+
dep_id: dep_id.clone(),
109+
envvar_name: dep.envvar_name.clone(),
110+
};
111+
dependencies.push(depends_on);
112+
}
113+
let dep_id = name_to_id
114+
.get(&execution.name)
115+
.ok_or(RegistrationError::InvalidSchema(
116+
"Execution name not found".to_string(),
117+
))?
118+
.clone();
119+
let priority = 0;
120+
let execution = registered::Execution {
121+
script: script,
122+
depends_on: dependencies,
123+
dep_id: dep_id,
124+
priority: priority,
125+
};
126+
executions.push(execution);
127+
}
128+
let procedure = registered::Procedure {
129+
runtime_texts,
130+
texts,
131+
empty_directories,
132+
executions,
133+
};
134+
Ok((
135+
procedure,
136+
content_to_id
137+
.iter()
138+
.map(|(k, v)| (v.clone(), k.clone()))
139+
.collect(),
140+
))
141+
}

lib/local_problem_registry/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "local_problem_registry"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
tokio = { workspace = true }
8+
futures = { workspace = true }
9+
judge_core = { path = "../judge_core" }

lib/local_problem_registry/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
pub mod registry_client;
2+
pub mod registry_server;
3+
4+
pub fn new_registry() -> (
5+
registry_server::RegistryServer,
6+
registry_client::RegistryClient,
7+
) {
8+
let registry = std::sync::Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new()));
9+
let server = registry_server::RegistryServer {
10+
registry: registry.clone(),
11+
};
12+
let client = registry_client::RegistryClient { registry };
13+
(server, client)
14+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use judge_core::*;
2+
use std::collections::HashMap;
3+
use std::sync::Arc;
4+
use tokio::sync::Mutex;
5+
6+
pub struct RegistryClient {
7+
pub(crate) registry: Arc<Mutex<HashMap<identifiers::ResourceId, String>>>,
8+
}
9+
10+
impl problem_registry::ProblemRegistryClient for RegistryClient {
11+
async fn fetch(
12+
&self,
13+
resource_id: identifiers::ResourceId,
14+
) -> Result<String, problem_registry::ResourceFetchError> {
15+
let registry = self.registry.lock().await;
16+
match registry.get(&resource_id) {
17+
Some(contents) => Ok(contents.clone()),
18+
None => Err(problem_registry::ResourceFetchError::NotFound(resource_id)),
19+
}
20+
}
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use judge_core::{problem_registry::*, procedure::*, writer_schema_transpiler::transpile, *};
2+
use std::collections::HashMap;
3+
use std::sync::Arc;
4+
use tokio::sync::Mutex;
5+
6+
pub struct RegistryServer {
7+
pub(crate) registry: Arc<Mutex<HashMap<identifiers::ResourceId, String>>>,
8+
}
9+
10+
impl ProblemRegistryServer for RegistryServer {
11+
async fn register(
12+
&self,
13+
procedure: writer_schema::Procedure,
14+
) -> Result<registered::Procedure, RegistrationError> {
15+
let (transpiled_procedure, content_to_register) = transpile(procedure)?;
16+
let mut registry = self.registry.lock().await;
17+
for (resource_id, content) in content_to_register {
18+
registry.insert(resource_id, content);
19+
}
20+
Ok(transpiled_procedure)
21+
}
22+
}

0 commit comments

Comments
 (0)