477b9a41dd
Workspace, leeres Crate grovewright-core und ein Wächter-Test, der den Quelltext des Kerns auf Verstöße gegen die Gleichschritt-Regeln prüft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
79 lines
2.4 KiB
Rust
79 lines
2.4 KiB
Rust
//! Guards the lockstep rules: the core must not use anything whose result
|
|
//! can differ between machines or between runs.
|
|
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
/// Text that must not appear anywhere in the source of the core, and why.
|
|
const FORBIDDEN: &[(&str, &str)] = &[
|
|
("f32", "floating point results can differ between machines"),
|
|
("f64", "floating point results can differ between machines"),
|
|
(
|
|
"HashMap",
|
|
"iteration order differs between runs; use BTreeMap or Vec",
|
|
),
|
|
(
|
|
"HashSet",
|
|
"iteration order differs between runs; use BTreeSet or Vec",
|
|
),
|
|
("std::time", "the core must not read the clock"),
|
|
("Instant", "the core must not read the clock"),
|
|
("SystemTime", "the core must not read the clock"),
|
|
("std::thread", "the core must stay single-threaded"),
|
|
("std::env", "the core must not depend on the environment"),
|
|
(
|
|
"std::fs",
|
|
"the core must not touch files; the shell does that",
|
|
),
|
|
(
|
|
"std::net",
|
|
"the core must not open connections; the shell does that",
|
|
),
|
|
("unsafe", "the core forbids unsafe code"),
|
|
];
|
|
|
|
fn rust_files(dir: &Path, out: &mut Vec<PathBuf>) {
|
|
for entry in fs::read_dir(dir).expect("source directory is readable") {
|
|
let path = entry.expect("directory entry is readable").path();
|
|
if path.is_dir() {
|
|
rust_files(&path, out);
|
|
} else if path.extension().is_some_and(|ext| ext == "rs") {
|
|
out.push(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn core_source_uses_no_forbidden_apis() {
|
|
let src = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
|
|
let mut files = Vec::new();
|
|
rust_files(&src, &mut files);
|
|
files.sort();
|
|
assert!(
|
|
!files.is_empty(),
|
|
"no source files found in {}",
|
|
src.display()
|
|
);
|
|
|
|
let mut problems = Vec::new();
|
|
for file in &files {
|
|
let text = fs::read_to_string(file).expect("source file is readable");
|
|
for (number, line) in text.lines().enumerate() {
|
|
for (needle, reason) in FORBIDDEN {
|
|
if line.contains(needle) {
|
|
problems.push(format!(
|
|
"{}:{}: `{needle}`: {reason}",
|
|
file.display(),
|
|
number + 1
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert!(
|
|
problems.is_empty(),
|
|
"forbidden APIs in the core:\n{}",
|
|
problems.join("\n")
|
|
);
|
|
}
|