use std::path::{Path, PathBuf};
use rustyfi_backend::{FontKey, FontMetrics, Length};
use rustyfi_lang::value::DocumentValue;
use rustyfi_loader::{LoadOptions, LoadedCst, LoadedFile, LoadedProgram};
use rustyfi_syntax::RustyfiVersion;
fn repo(rel: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join(rel)
}
struct Mono;
impl FontMetrics for Mono {
fn advance(&self, _f: FontKey, c: char, size: Length) -> Option<Length> {
if c.is_ascii() {
Some(size * 0.5)
} else {
None
}
}
fn ascender(&self, _f: FontKey, size: Length) -> Length {
size * 0.75
}
fn descender(&self, _f: FontKey, size: Length) -> Length {
size * 0.25
}
}
fn merge_program_006(program: LoadedProgram) -> rustyfi_syntax::cst::File {
fn as_v006(cst: LoadedCst) -> rustyfi_syntax::cst::File {
match cst {
LoadedCst::V0_0(f) => f,
LoadedCst::V0_1(_) => unreachable!("this test's 0.0.6 merge path only"),
}
}
let mut files = program.files;
let entry = files.pop().expect("loader always yields the entry last");
let entry_cst = as_v006(entry.cst);
let mut prelude = Vec::new();
for lib in files {
prelude.extend(as_v006(lib.cst).prelude);
}
prelude.extend(entry_cst.prelude);
rustyfi_syntax::cst::File {
headers: Vec::new(),
prelude,
in_kw: entry_cst.in_kw,
body: entry_cst.body,
eoi: entry_cst.eoi,
}
}
#[test]
fn v01_document_compiles_and_matches_0_0_twin_geometry() {
let lib_src = std::fs::read_to_string(repo("lib-rustyfi/dist-v01/packages/v01-mini.satyh"))
.expect("read v01-mini.satyh");
let entry_src =
std::fs::read_to_string(repo("crates/rustyfi/tests/fixtures/v01-minimal.saty"))
.expect("read v01-minimal.saty");
let files = vec![
LoadedFile {
path: repo("lib-rustyfi/dist-v01/packages/v01-mini.satyh"),
cst: LoadedCst::V0_1(
rustyfi_syntax::parse_file_v1(&lib_src)
.unwrap_or_else(|e| panic!("v01-mini.satyh: {e}")),
),
origin: Default::default(),
version: RustyfiVersion::V0_1,
},
LoadedFile {
path: repo("crates/rustyfi/tests/fixtures/v01-minimal.saty"),
cst: LoadedCst::V0_1(
rustyfi_syntax::parse_file_v1(&entry_src)
.unwrap_or_else(|e| panic!("v01-minimal.saty: {e}")),
),
origin: Default::default(),
version: RustyfiVersion::V0_1,
},
];
let mono = Mono;
let (doc_v1, trials) = rustyfi_lang::compile_document_v1_with_trials(&files, &mono)
.unwrap_or_else(|e| panic!("compile_document_v1_with_trials: {e}"));
assert_eq!(
trials, 1,
"no cross-references in this fixture — one trial suffices"
);
assert_eq!(doc_v1.pages.len(), 1);
assert!(
doc_v1.pages[0].lines.len() >= 4,
"three +p paragraphs + the footer line, got {}",
doc_v1.pages[0].lines.len()
);
let entry_006 = repo("crates/rustyfi/tests/fixtures/v01-equiv-006.saty");
let lib_root_006 = repo("lib-rustyfi");
let program_006 = rustyfi_loader::load(
&entry_006,
&LoadOptions {
lib_root: Some(lib_root_006),
version: RustyfiVersion::V0_0,
..Default::default()
},
)
.unwrap_or_else(|e| panic!("loading v01-equiv-006.saty: {e}"));
let merged_006 = merge_program_006(program_006);
let doc_006 = rustyfi_lang::compile_document_cst(&merged_006, &mono)
.unwrap_or_else(|e| panic!("compile_document_cst on the 0.0.6 twin: {e}"));
assert_geometry_equivalent(&doc_v1, &doc_006);
}
fn assert_geometry_equivalent(a: &DocumentValue, b: &DocumentValue) {
assert_eq!(
a.geometry, b.geometry,
"page geometry must match exactly (both are A4)"
);
assert_eq!(a.pages.len(), b.pages.len(), "page count must match");
for (i, (pa, pb)) in a.pages.iter().zip(b.pages.iter()).enumerate() {
assert_eq!(
pa.lines.len(),
pb.lines.len(),
"page {i}: line count must match"
);
for (j, (la, lb)) in pa.lines.iter().zip(pb.lines.iter()).enumerate() {
assert!(
(la.x.0 - lb.x.0).abs() < 1e-6,
"page {i} line {j}: x mismatch ({} vs {})",
la.x.0,
lb.x.0
);
assert!(
(la.baseline_y.0 - lb.baseline_y.0).abs() < 1e-6,
"page {i} line {j}: baseline_y mismatch ({} vs {})",
la.baseline_y.0,
lb.baseline_y.0
);
}
}
}