holger-estate-front/tests/the_front_compiles_only_its_own_files.rs
//! **The front compiles only its own files** (A28 L12): a `#[path]` to another crate's source
//! (holger-server's `server/cli/src/dev_narrate.rs` was one) is a dependency the manifest does
//! not name: its features, its tests and its deps are the other crate's, and a move there breaks
//! this build. Shared code is a crate (`holger-dev-narrate`), named in Cargo.toml.
use std::path::{Path, PathBuf};
fn rust_files(dir: &Path, out: &mut Vec<PathBuf>) {
for e in std::fs::read_dir(dir).unwrap_or_else(|e| panic!("{}: {e}", dir.display())) {
let p = e.unwrap().path();
if p.is_dir() {
rust_files(&p, out);
} else if p.extension().is_some_and(|x| x == "rs") {
out.push(p);
}
}
}
/// Pure: every `#[path = "…"]` in `text` whose target leaves `crate_dir` (resolved from `file`'s
/// directory), as `(line, target)`.
fn escaping_paths(crate_dir: &Path, file: &Path, text: &str) -> Vec<(usize, String)> {
let base = file.parent().unwrap();
let crate_dir = normalize(crate_dir);
text.lines()
.enumerate()
.filter_map(|(n, l)| {
let l = l.trim_start();
let rest = l
.strip_prefix("#[path")?
.trim_start()
.strip_prefix('=')?
.trim_start()
.strip_prefix('"')?;
let target = &rest[..rest.find('"')?];
(!normalize(&base.join(target)).starts_with(&crate_dir))
.then(|| (n + 1, target.to_string()))
})
.collect()
}
fn normalize(p: &Path) -> PathBuf {
let mut out = PathBuf::new();
for c in p.components() {
match c {
std::path::Component::ParentDir => {
out.pop();
}
std::path::Component::CurDir => {}
c => out.push(c),
}
}
out
}
#[test]
fn no_source_of_another_crate_is_compiled_in_by_path() {
let dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let mut files = Vec::new();
for sub in ["src", "tests"] {
rust_files(&dir.join(sub), &mut files);
}
assert!(
files.len() > 5,
"the scan read the front's sources: {files:?}"
);
let found: Vec<String> = files
.iter()
.flat_map(|f| {
let text = std::fs::read_to_string(f).unwrap();
escaping_paths(dir, f, &text)
.into_iter()
.map(move |(n, t)| format!("{}:{n} #[path = {t:?}]", f.display()))
})
.collect();
assert!(
found.is_empty(),
"the front compiles another crate's file; depend on the crate instead:\n{}",
found.join("\n")
);
}
#[test]
fn a_path_inside_the_crate_is_fine_and_one_outside_is_found() {
let dir = Path::new("/r/holger-estate-front");
let f = dir.join("src/main.rs");
assert!(escaping_paths(dir, &f, "#[path = \"inner/x.rs\"]\nmod x;").is_empty());
assert_eq!(
escaping_paths(
dir,
&f,
"use a;\n#[path = \"../../server/cli/src/dev_narrate.rs\"]\nmod dev_narrate;"
),
vec![(2, "../../server/cli/src/dev_narrate.rs".to_string())]
);
}