79 lines
2.5 KiB
Rust
79 lines
2.5 KiB
Rust
use std::ffi::CString;
|
|
|
|
use pyo3::prelude::*;
|
|
use pyo3::types::{PyList, PyModule};
|
|
|
|
const VALIDATOR_CODE: &str = include_str!("../shared_state_validator.py");
|
|
|
|
fn validator_module<'py>(py: Python<'py>) -> PyResult<Bound<'py, PyModule>> {
|
|
let code = CString::new(VALIDATOR_CODE).expect("validator source contains a null byte");
|
|
PyModule::from_code(
|
|
py,
|
|
&code,
|
|
c"shared_state_validator.py",
|
|
c"shared_state_validator",
|
|
)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn validate_source(
|
|
source: &str,
|
|
filename: &str,
|
|
class_names: &[&str],
|
|
) -> Result<Vec<String>, String> {
|
|
Python::attach(|py| {
|
|
let module = validator_module(py).map_err(|error| error.to_string())?;
|
|
let class_names = PyList::new(py, class_names).map_err(|error| error.to_string())?;
|
|
module
|
|
.getattr("validate_source")
|
|
.and_then(|function| function.call1((source, filename, class_names)))
|
|
.and_then(|result| result.extract::<Vec<String>>())
|
|
.map_err(|error| error.to_string())
|
|
})
|
|
}
|
|
|
|
fn validate_solution(path: &str, class_names: &[&str]) -> Result<Vec<String>, String> {
|
|
Python::attach(|py| {
|
|
let module = validator_module(py).map_err(|error| error.to_string())?;
|
|
let class_names = PyList::new(py, class_names).map_err(|error| error.to_string())?;
|
|
module
|
|
.getattr("validate_solution")
|
|
.and_then(|function| function.call1((path, class_names)))
|
|
.and_then(|result| result.extract::<Vec<String>>())
|
|
.map_err(|error| error.to_string())
|
|
})
|
|
}
|
|
|
|
pub fn validate_or_report(path: &str, class_names: &[&str]) -> bool {
|
|
match validate_solution(path, class_names) {
|
|
Ok(violations) if violations.is_empty() => true,
|
|
Ok(violations) => {
|
|
println!("ERROR: shared state between AnySystem processes is forbidden");
|
|
for violation in violations {
|
|
println!("{violation}");
|
|
}
|
|
println!("\nSCORE: 0\n");
|
|
false
|
|
}
|
|
Err(error) => panic!("failed to validate solution for shared state: {error}"),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::validate_source;
|
|
|
|
#[test]
|
|
fn embeds_validator_and_rejects_shared_state() {
|
|
let violations = validate_source(
|
|
"class ProcessImpl:\n shared = {}\n",
|
|
"solution.py",
|
|
&["ProcessImpl"],
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(violations.len(), 1);
|
|
assert!(violations[0].contains("ProcessImpl.shared"));
|
|
}
|
|
}
|