Skip to content

Commit

Permalink
Clear task.yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
Virv12 committed Sep 23, 2024
1 parent b57ee7a commit 5f9b3b3
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 63 deletions.
36 changes: 6 additions & 30 deletions task-maker-format/src/ioi/format/italian_yaml/cases_gen.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Write;
use std::io::Read;
use std::fmt::{Debug, Display, Formatter, Write};
use std::fs;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
Expand All @@ -13,16 +10,13 @@ use pest::Parser;

use task_maker_diagnostics::CodeSpan;

use crate::ioi::format::italian_yaml::TaskInputEntry;
use crate::ioi::italian_yaml::{is_tm_deletable, TaskInputEntry, TM_ALLOW_DELETE_COOKIE};
use crate::ioi::{
InputGenerator, InputValidator, OutputGenerator, SubtaskId, SubtaskInfo, TestcaseId,
TestcaseInfo, TM_VALIDATION_FILE_NAME,
};
use crate::SourceFile;

/// String placed in the auto-generated gen/GEN marking it as safely deletable.
pub(crate) const TM_ALLOW_DELETE_COOKIE: &str = "tm-allow-delete";

/// This module exists because of a `pest`'s bug: <https://github.com/pest-parser/pest/issues/326>
#[allow(missing_docs)]
mod parser {
Expand Down Expand Up @@ -146,7 +140,7 @@ where
.context("Invalid gen/cases.gen path")?
.parent()
.context("Invalid gen/cases.gen path")?;
let content = std::fs::read_to_string(path)
let content = fs::read_to_string(path)
.with_context(|| format!("Failed to read {}", path.display()))?;
let mut file = parser::CasesGenParser::parse(parser::Rule::file, &content)
.context("Cannot parse cases.gen")?;
Expand Down Expand Up @@ -209,7 +203,7 @@ where
/// Write an auto-generated version of the gen/GEN file inside the task directory.
pub(crate) fn write_gen_gen(&self) -> Result<(), Error> {
let dest = self.task_dir.join("gen/GEN");
if dest.exists() && !is_gen_gen_deletable(&dest)? {
if dest.exists() && !is_tm_deletable(&dest)? {
warn!(
"The gen/GEN file does not contain {}. Won't overwrite",
TM_ALLOW_DELETE_COOKIE
Expand Down Expand Up @@ -256,7 +250,7 @@ where
}
}
}
std::fs::write(self.task_dir.join("gen/GEN"), gen).context("Failed to write gen/GEN")?;
fs::write(self.task_dir.join("gen/GEN"), gen).context("Failed to write gen/GEN")?;
Ok(())
}

Expand Down Expand Up @@ -668,24 +662,6 @@ where
}
}

/// Check if the gen/GEN file is deletable, i.e. it exists and it is autogenerated.
pub(crate) fn is_gen_gen_deletable(path: &Path) -> Result<bool, Error> {
if !path.exists() {
return Ok(false);
}
let mut buffer = vec![];
let mut file = std::fs::File::open(path)
.with_context(|| format!("Cannot open file {}", path.display()))?;
file.read_to_end(&mut buffer)
.context("Cannot read gen/GEN file")?;
let content = String::from_utf8_lossy(&buffer);
// if the gen/GEN is present and it does not contain the cookie, skip all the generation
if !content.contains(TM_ALLOW_DELETE_COOKIE) {
return Ok(false);
}
Ok(true)
}

impl ConstraintOperator {
/// Apply the operator to the provided values and return the result of the comparison.
fn is_valid(&self, lhs: i64, rhs: i64) -> bool {
Expand Down
86 changes: 55 additions & 31 deletions task-maker-format/src/ioi/format/italian_yaml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@
//! The subtask also does not have a name, the default one (`subtask2`) will be used.
use std::collections::HashMap;
use std::fs::File;
use std::io::BufWriter;
use std::fs::{self, File};
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use std::sync::Arc;

Expand All @@ -267,7 +267,6 @@ use serde::{Deserialize, Serialize, Serializer};
use unic::normal::StrNormalForm;
use unic::ucd::category::GeneralCategory;

pub(crate) use cases_gen::{is_gen_gen_deletable, TM_ALLOW_DELETE_COOKIE};
use task_maker_lang::GraderMap;

use crate::ioi::sanity_checks::get_sanity_checks;
Expand All @@ -283,6 +282,9 @@ mod cases_gen;
mod gen_gen;
mod static_inputs;

/// String placed in the auto-generated gen/GEN marking it as safely deletable.
pub(crate) const TM_ALLOW_DELETE_COOKIE: &str = "tm-allow-delete";

/// The set of valid Unicode General Categories for the characters composing a subtask name.
pub const VALID_SUBTASK_NAME_CHARACTER_CATEGORIES: &[GeneralCategory] = &[
// L group (included in XID_Start)
Expand Down Expand Up @@ -500,21 +502,25 @@ pub fn parse_task<P: AsRef<Path>>(
) -> Result<IOITask, Error> {
let task_dir = task_dir.as_ref();

let task_yaml_path = task_dir.join("task.yaml");
let task_yaml_orig_path = task_dir.join("task.yaml.orig");
let task_yaml_overwrite: bool;
let mut yaml: TaskYAML;
if task_dir.join("task.yaml.orig").exists() {
if task_yaml_orig_path.exists() {
task_yaml_overwrite = true;
let path = task_dir.join(task_dir.join("task.yaml.orig"));
let file = File::open(&path)
.with_context(|| format!("Cannot open task.yaml.orig from {}", path.display()))?;
let file = File::open(&task_yaml_orig_path).with_context(|| {
format!(
"Cannot open task.yaml.orig from {}",
task_yaml_orig_path.display()
)
})?;
let yaml_orig: TaskYAMLOrig =
serde_yaml::from_reader(file).context("Failed to deserialize task.yaml.orig")?;
yaml = yaml_orig.into_task_yaml(task_dir);
} else if task_dir.join("task.yaml").exists() {
} else if task_yaml_path.exists() {
task_yaml_overwrite = false;
let path = task_dir.join(task_dir.join("task.yaml"));
let file = File::open(&path)
.with_context(|| format!("Cannot open task.yaml from {}", path.display()))?;
let file = File::open(&task_yaml_path)
.with_context(|| format!("Cannot open task.yaml from {}", task_yaml_path.display()))?;
yaml = serde_yaml::from_reader(file).context("Failed to deserialize task.yaml")?;
} else {
bail!("No task.yaml found in {}", task_dir.display());
Expand Down Expand Up @@ -630,27 +636,37 @@ pub fn parse_task<P: AsRef<Path>>(
yaml.score_type = Some(testcase_score_aggregator);

if task_yaml_overwrite {
if !eval_config.dry_run {
yaml.score_type_parameters = Some(
subtasks
.iter()
.sorted_by_key(|(id, _)| *id)
.map(|(_, st)| {
let testcases = st
.testcases
.iter()
.map(|tc_num| format!("{tc_num:03}"))
.join("|");
(st.max_score, testcases)
})
.collect(),
if !task_yaml_path.exists() || is_tm_deletable(&task_yaml_path)? {
if !eval_config.dry_run {
yaml.score_type_parameters = Some(
subtasks
.iter()
.sorted_by_key(|(id, _)| *id)
.map(|(_, st)| {
let testcases = st
.testcases
.iter()
.map(|tc_num| format!("{tc_num:03}"))
.join("|");
(st.max_score, testcases)
})
.collect(),
);

let file = File::create(&task_yaml_path).with_context(|| {
format!("Cannot open task.yaml from {}", task_yaml_path.display())
})?;
let mut buf_file = BufWriter::new(file);
writeln!(buf_file, "# Generated by task-maker. Do not edit!")?;
writeln!(buf_file, "# {}", TM_ALLOW_DELETE_COOKIE)?;
writeln!(buf_file, "# Removing or changing the line above will prevent task-maker from touching this file again.")?;
serde_yaml::to_writer(buf_file, &yaml).context("Failed to serialize task.yaml")?;
}
} else {
warn!(
"The task.yaml file does not contain {}. Won't overwrite",
TM_ALLOW_DELETE_COOKIE
);

let path = task_dir.join("task.yaml");
let file = File::create(&path)
.with_context(|| format!("Cannot open task.yaml from {}", path.display()))?;
serde_yaml::to_writer(BufWriter::new(file), &yaml)
.context("Failed to serialize task.yaml")?;
}
} else if subtasks.values().any(|st| !st.dependencies.is_empty()) {
bail!("Use task.yaml.orig to use subtask dependencies");
Expand Down Expand Up @@ -692,6 +708,14 @@ pub fn parse_task<P: AsRef<Path>>(
Ok(task)
}

/// Check if the file is deletable, i.e. it contains the TM_ALLOW_DELETE_COOKIE
/// Assumes the file exists.
pub(crate) fn is_tm_deletable(path: &Path) -> Result<bool, Error> {
let buffer = fs::read(path).with_context(|| format!("Cannot read file {}", path.display()))?;
let content = String::from_utf8_lossy(&buffer);
Ok(content.contains(TM_ALLOW_DELETE_COOKIE))
}

/// Search for a valid input validator inside the task directory. Will return a function that, given
/// a subtask id, returns an `InputValidator` using that validator. If no validator is found,
/// `InputValidator::AssumeValid` is used.
Expand Down
20 changes: 18 additions & 2 deletions task-maker-format/src/ioi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use task_maker_lang::GraderMap;
pub use ui_state::*;

use crate::ioi::format::italian_yaml::TM_ALLOW_DELETE_COOKIE;
use crate::ioi::italian_yaml::is_gen_gen_deletable;
use crate::ioi::italian_yaml::is_tm_deletable;
use crate::sanity_checks::SanityChecks;
use crate::solution::SolutionInfo;
use crate::ui::*;
Expand Down Expand Up @@ -481,7 +481,7 @@ impl IOITask {
let gen_gen_path = self.path.join("gen/GEN");
let cases_gen_path = self.path.join("gen/cases.gen");
if cases_gen_path.exists() && gen_gen_path.exists() {
if is_gen_gen_deletable(&gen_gen_path)? {
if is_tm_deletable(&gen_gen_path)? {
info!("Removing {}", gen_gen_path.display());
std::fs::remove_file(&gen_gen_path).with_context(|| {
format!("Failed to remove gen/GEN at {}", gen_gen_path.display())
Expand All @@ -493,6 +493,22 @@ impl IOITask {
);
}
}
// remove task.yaml if there is task.yaml.orig
let task_yaml_path = self.path.join("task.yaml");
let task_yaml_orig_path = self.path.join("task.yaml.orig");
if task_yaml_orig_path.exists() && task_yaml_path.exists() {
if is_tm_deletable(&task_yaml_path)? {
info!("Removing {}", task_yaml_path.display());
std::fs::remove_file(&task_yaml_path).with_context(|| {
format!("Failed to remove task.yaml at {}", task_yaml_path.display())
})?;
} else {
warn!(
"Won't remove task.yaml since it doesn't contain {}",
TM_ALLOW_DELETE_COOKIE
);
}
}
Ok(())
}

Expand Down

0 comments on commit 5f9b3b3

Please sign in to comment.