Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/one.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,28 @@ trim_output = true
max_memory = { compile = 128, run = 64 }
max_file_size = 8192

[workspaces.gleam_env]
# from = "./some_dir"
setup = '''
yes | gleam new .
gleam add dateformat@1
gleam add birl
'''

[languages]
python3 = "latest"
java = "21"
javascript = "deno:latest"
ocaml = { build = "ocamlc -o out solution.ml", run = "./out", source_file = "solution.ml" }
haskell = { build = "ghc solution.hs", run = "./solution", source_file = "solution.hs", syntax = "haskell" }

[languages.gleam]
build = "gleam build"
run = "gleam run"
workspace = "gleam_env"
source_file = "src/main.gleam"
syntax = "elixir"

[[accounts.hosts]]
name = "Teacher"
password = "abc123"
Expand Down
5 changes: 5 additions & 0 deletions src/language/language_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,15 @@ impl<'de> Visitor<'de> for LanguageMapVisitor {
display_name,
build,
run,
workspace,
source_file,
syntax,
} => Language::Custom {
name: key.clone().into_owned(),
display_name: display_name.unwrap_or_else(|| key.clone()).into_owned(),
build: build.map(Cow::into_owned),
run: run.into_owned(),
workspace: workspace.map(Cow::into_owned),
syntax: syntax
.or_else(|| Syntax::from_string::<M::Error>(key).ok())
.unwrap_or_default(),
Expand Down Expand Up @@ -147,6 +149,7 @@ impl Serialize for LanguageSet {
name,
display_name,
build,
workspace,
run,
source_file,
syntax,
Expand All @@ -157,6 +160,7 @@ impl Serialize for LanguageSet {
display_name: Some(display_name.into()),
build: build.as_ref().map(Into::into),
run: run.into(),
workspace: workspace.as_ref().map(Cow::from),
source_file: source_file.into(),
syntax: Some(*syntax),
},
Expand All @@ -182,6 +186,7 @@ enum TomlLanguage<'a> {
display_name: Option<Cow<'a, str>>,
build: Option<Cow<'a, str>>,
run: Cow<'a, str>,
workspace: Option<Cow<'a, str>>,
source_file: Cow<'a, str>,
syntax: Option<Syntax>,
},
Expand Down
12 changes: 12 additions & 0 deletions src/language/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ pub enum Language {
run: String,
source_file: String,
syntax: Syntax,
workspace: Option<String>,
},
}

Expand Down Expand Up @@ -368,4 +369,15 @@ impl Language {
Language::Custom { syntax, .. } => *syntax,
}
}

pub fn workspace(&self) -> Option<&str> {
match self {
Language::BuiltIn { .. } => None,
Language::Custom {
workspace: Some(workspace),
..
} => Some(workspace),
_ => None,
}
}
}
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
collections::BTreeMap,
hash::{DefaultHasher, Hasher},
io::Read,
path::PathBuf,
Expand All @@ -13,13 +14,16 @@ use roi::RawOrImport;
use serde::{Deserialize, Serialize};
use typst::foundations::{Array, Dict, IntoValue, Str, Value};

use crate::workspaces::Workspace;

mod custom_serde;
pub mod integrations;
pub mod language;
pub mod packet;
pub mod render;
pub mod roi;
pub mod scoring;
pub mod workspaces;

mod util;

Expand Down Expand Up @@ -350,6 +354,9 @@ pub struct Config {
pub integrations: Integrations,
/// Maximum number of attempts that a user is allowed to make for a given problem
pub max_submissions: Option<std::num::NonZero<u32>>,
/// Map of workspaces that function as reusable execution environments
#[serde(default)]
pub workspaces: RawOrImport<BTreeMap<String, Workspace>>,
/// List of languages available for the server
pub languages: RawOrImport<LanguageSet>,
/// Accounts that will be granted access to the server
Expand Down Expand Up @@ -635,6 +642,7 @@ impl Default for Config {
game: Default::default(),
max_submissions: None,
languages: Default::default(),
workspaces: Default::default(),
accounts: Default::default(),
packet: Default::default(),
test_runner: Default::default(),
Expand Down
15 changes: 15 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ fn packet_files_parse_correctly() -> Result<()> {
Some(&Language::Custom {
name: "ocaml".into(),
display_name: "ocaml".into(),
workspace: None,
build: Some("ocamlc -o out solution.ml".into()),
run: "./out".into(),
source_file: "solution.ml".into(),
Expand All @@ -50,6 +51,7 @@ fn packet_files_parse_correctly() -> Result<()> {
Some(&Language::Custom {
name: "haskell".into(),
display_name: "haskell".into(),
workspace: None,
build: Some("ghc solution.hs".into()),
run: "./solution".into(),
source_file: "solution.hs".into(),
Expand All @@ -65,6 +67,19 @@ fn packet_files_parse_correctly() -> Result<()> {
config.languages.get_by_str("javascript")
);

assert_eq!(
Some(&Language::Custom {
name: "gleam".into(),
display_name: "gleam".into(),
workspace: Some("gleam_env".into()),
build: Some("gleam build".into()),
run: "gleam run".into(),
source_file: "src/main.gleam".into(),
syntax: Syntax::Elixir,
}),
config.languages.get_by_str("gleam")
);

dbg!(config.hash());

Ok(())
Expand Down
15 changes: 15 additions & 0 deletions src/workspaces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use std::path::PathBuf;

use serde::{Deserialize, Serialize};

use crate::roi::RawOrImport;

#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct Workspace {
/// Optional directory on which to base the generation of this workspace
from: Option<PathBuf>,
/// Script executed to generate the workspace
setup: Option<RawOrImport<String>>,
/// Script executed to install any required dependencies if any are required
install: Option<RawOrImport<String>>,
}
12 changes: 12 additions & 0 deletions tests/events.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ max_file_size = 8192
event_handlers = ["./tests/on-score.js", "./tests/on-complete.ts"]
webhooks = ["http://localhost:9090", "http://localhost:8090"]

[workspaces]
gleam-env = { setup = "yes | gleam new ." }

# A more complex workspace with a base, gleam project, and dependencies
[workspaces.gleam-env-two]
from = "./some_dir" # some directory to act as base (perhaps you want some files pre-included)
setup = '''
yes | gleam new .
gleam add dateformat@1
gleam add birl
'''

[game]
score = "points - 2*max(completed, attempts)"

Expand Down
3 changes: 3 additions & 0 deletions tests/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fn hash_consistent() {
game: Default::default(),
max_submissions: None,
languages: Default::default(),
workspaces: Default::default(),
accounts: Default::default(),
packet: Default::default(),
test_runner: Default::default(),
Expand All @@ -29,6 +30,7 @@ fn port_diff() {
game: Default::default(),
max_submissions: None,
languages: Default::default(),
workspaces: Default::default(),
accounts: Default::default(),
packet: Default::default(),
test_runner: Default::default(),
Expand All @@ -42,6 +44,7 @@ fn port_diff() {
integrations: Default::default(),
max_submissions: None,
languages: Default::default(),
workspaces: Default::default(),
accounts: Default::default(),
packet: Default::default(),
test_runner: Default::default(),
Expand Down