Skip to content

Commit

Permalink
Implement planned features
Browse files Browse the repository at this point in the history
  • Loading branch information
Bubbler-4 committed May 29, 2024
1 parent 26fce62 commit 37f4a8a
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 44 deletions.
62 changes: 58 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ dialoguer = { version = "0.11.0", features = ["history"], default-features = fal
indicatif = "0.17.8"
once_cell = "1.19.0"
regex = "1.10.4"
serde = "1.0.203"
serde_json = "1.0.117"
similar = "2.5.0"
thirtyfour = "0.32.0"
tokio = { version = "1.37.0", features = ["time", "process", "rt"] }
toml = "0.8.13"
9 changes: 5 additions & 4 deletions src/command.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod parser;
mod executor;

use crate::data::Credentials;

#[derive(Debug, Clone)]
pub(crate) struct InputCommand {
raw_command: String,
Expand Down Expand Up @@ -35,6 +37,7 @@ impl std::str::FromStr for InputCommand {
#[derive(Debug, Clone)]
pub(crate) enum Command {
Set(Setting),
Preset { name: String },
Prob { prob: String },
Build { build: Option<String> },
Run {
Expand All @@ -55,12 +58,10 @@ pub(crate) enum Command {

#[derive(Debug, Clone)]
pub(crate) enum Setting {
Credentials {
bojautologin: String,
onlinejudge: String,
},
Credentials(Credentials),
Lang(String),
File(String),
Init(String),
Build(String),
Cmd(String),
Input(String),
Expand Down
77 changes: 71 additions & 6 deletions src/command/executor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{Command, Setting, CommandExecuteError};
use super::{Command, Setting, CommandExecuteError, Credentials};
use crate::global_state::GlobalState;
use crate::data::{ProblemId, ExampleIO};
use crate::data::{ProblemId, ExampleIO, Preset};
use crate::infra::subprocess::{run_silent, run_with_input_timed, run_interactive, Output};
use regex::{Regex, Captures, Replacer};
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -43,6 +43,13 @@ impl GlobalState {
pub(crate) fn execute(&mut self, command: &Command) -> anyhow::Result<()> {
match command {
Command::Set(setting) => self.set(setting)?,
Command::Preset { name } => {
let Some(preset) = self.presets.get(name) else {
error!("preset: Unknown preset name")?
};
let preset = preset.clone();
self.preset(preset)?;
}
Command::Prob { prob } => self.prob(prob)?,
Command::Build { build } => {
let Some(prob) = self.problem.as_ref().map(|p| &p.id) else {
Expand Down Expand Up @@ -130,7 +137,7 @@ impl GlobalState {

fn set(&mut self, setting: &Setting) -> anyhow::Result<()> {
match setting {
Setting::Credentials { bojautologin, onlinejudge } => {
Setting::Credentials(Credentials { bojautologin, onlinejudge }) => {
self.credentials.bojautologin.clear();
self.credentials.bojautologin += bojautologin;
self.credentials.onlinejudge.clear();
Expand All @@ -150,6 +157,11 @@ impl GlobalState {
self.file.clear();
self.file += file;
}
Setting::Init(init) => {
self.init.clear();
self.init += init;
self.init()?;
}
Setting::Build(build) => {
self.build.clear();
self.build += build;
Expand All @@ -166,14 +178,63 @@ impl GlobalState {
Ok(())
}

fn preset(&mut self, preset: Preset) -> anyhow::Result<()> {
let Preset { credentials, lang, file, init, build, cmd, input, .. } = preset;
if let Some(credentials) = credentials {
self.set(&Setting::Credentials(credentials))?;
}
if let Some(lang) = lang {
self.set(&Setting::Lang(lang))?;
}
if let Some(file) = file {
self.set(&Setting::File(file))?;
}
if let Some(init) = init {
self.set(&Setting::Init(init))?;
}
if let Some(build) = build {
self.set(&Setting::Build(build))?;
}
if let Some(cmd) = cmd {
self.set(&Setting::Cmd(cmd))?;
}
if let Some(input) = input {
self.set(&Setting::Input(input))?;
}
Ok(())
}

fn prob(&mut self, prob: &str) -> anyhow::Result<()> {
// TODO: try fetching from local datastore first
let problem_id = prob.parse::<ProblemId>()?;
self.problem = Some(self.browser.get_problem(&problem_id)?);
if let Some(problem) = self.problem_cache.get(&problem_id) {
// try copying from the cache first
self.problem = Some(problem.clone());
} else {
// store the fetched problem to the cache
self.problem = Some(self.browser.get_problem(&problem_id)?);
self.problem_cache.insert(problem_id, self.problem.clone().unwrap());
}
let problem = self.problem.as_ref().unwrap();
println!("Problem {} {}", problem.id, problem.title);
println!("Time limit: {:.3}s{} / Memory limit: {}MB{}", problem.time, if !problem.time_bonus { " (No bonus)" } else { "" }, problem.memory, if !problem.memory_bonus { " (No bonus)" } else { "" });
// TODO: store the fetched problem to the local datastore
self.init()?;
Ok(())
}

fn init(&self) -> anyhow::Result<()> {
// if init is empty, do nothing
if self.init.is_empty() {
return Ok(());
}
// if prob is not set, do not try to run init
let Some(prob) = self.problem.as_ref() else {
return Ok(());
};
let init_cmd = substitute_problem(&self.init, &prob.id);
let res = run_silent(&init_cmd)?;
if let Some(err) = res {
error!("Init returned nonzero exit code. STDERR:\n{}", err)?
}
Ok(())
}

Expand Down Expand Up @@ -289,12 +350,14 @@ set credentials <bojautologin> <onlinejudge>
Set BOJ login cookies and log in with them.
set lang <lang>
set file <file>
set init <init>
set build <build>
set cmd <cmd>
set input <input>
Set default value for the given variable.
prob <prob>
Load the problem <prob> and set it as the current problem.
If <init> is set, run it.
build [build]
Build your solution.
run [i=input] [c=cmd]
Expand All @@ -303,6 +366,8 @@ test [c=cmd]
Test your solution against sample test cases.
submit [l=lang] [f=file]
Submit your solution to BOJ.
preset <name>
Apply one of the presets defined in boj.toml.
help
Display this help.
exit
Expand Down
22 changes: 18 additions & 4 deletions src/command/parser.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Command, Setting, CommandParseError};
use super::{Command, Setting, CommandParseError, Credentials};
use std::collections::HashMap;

macro_rules! error {
Expand Down Expand Up @@ -175,12 +175,12 @@ impl std::str::FromStr for Command {
} else if args.len() > 3 {
return error!("set credentials: Too many arguments");
}
Setting::Credentials {
Setting::Credentials(Credentials {
bojautologin: args[1].clone(),
onlinejudge: args[2].clone(),
}
})
}
"lang" | "file" | "build" | "cmd" | "input" => {
"lang" | "file" | "build" | "cmd" | "input" | "init" => {
if args.len() == 1 {
return error!("set {}: Missing argument <{}>", variable, variable);
} else if args.len() > 2 {
Expand All @@ -190,6 +190,7 @@ impl std::str::FromStr for Command {
match variable {
"lang" => Setting::Lang(arg),
"file" => Setting::File(arg),
"init" => Setting::Init(arg),
"build" => Setting::Build(arg),
"cmd" => Setting::Cmd(arg),
"input" => Setting::Input(arg),
Expand All @@ -205,6 +206,19 @@ impl std::str::FromStr for Command {
}
Ok(Command::Set(setting))
}
"preset" => {
if args.len() == 0 {
error!("preset: Missing argument <name>")
} else if args.len() > 1 {
error!("preset: Too many positional arguments")
} else if kwargs.len() > 0 {
error!("preset: Unexpected keyword argument(s)")
} else {
Ok(Self::Preset {
name: args[0].clone()
})
}
}
"prob" => {
if args.len() == 0 {
error!("prob: Missing argument <problem>")
Expand Down
25 changes: 22 additions & 3 deletions src/data.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) enum ProblemId {
Problem(String),
ContestProblem(String)
Expand Down Expand Up @@ -58,7 +58,7 @@ impl ProblemId {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct ExampleIO {
pub(crate) input: String,
pub(crate) output: String,
Expand Down Expand Up @@ -146,7 +146,7 @@ impl ProblemKind {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct Problem {
pub(crate) id: ProblemId,
pub(crate) title: String,
Expand All @@ -158,7 +158,26 @@ pub(crate) struct Problem {
pub(crate) io: Vec<ExampleIO>,
}

#[derive(Debug, Clone, serde::Deserialize)]
pub(crate) struct Credentials {
pub(crate) bojautologin: String,
pub(crate) onlinejudge: String,
}

#[derive(Clone, serde::Deserialize)]
pub(crate) struct Preset {
pub(crate) name: String,
pub(crate) credentials: Option<Credentials>,
pub(crate) lang: Option<String>,
pub(crate) file: Option<String>,
pub(crate) init: Option<String>,
pub(crate) build: Option<String>,
pub(crate) cmd: Option<String>,
pub(crate) input: Option<String>,
}

#[derive(serde::Deserialize)]
pub(crate) struct BojConfig {
pub(crate) start: Option<String>,
pub(crate) preset: Vec<Preset>,
}
Loading

0 comments on commit 37f4a8a

Please sign in to comment.