From 06db4a9e46f4b4cc649aec7e24789e24c7b27305 Mon Sep 17 00:00:00 2001 From: Robert Danaraj Date: Mon, 5 Oct 2026 08:01:52 -0700 Subject: [PATCH] feat: add modular compile-time rule packaging for embeddable MathCAT --- Cargo.toml | 9 +++++++++ src/bin/package_rules.rs | 35 +++++++++++++++++++++++++++++++++++ src/rules_archive.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a51507536..ae417a70f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,21 @@ exclude = ["src/main.rs", "docs", "BrailleDocs", "PythonScripts"] # should ha [features] +"default" = [] "rule-coverage" = ["dep:serde_json"] "include-zip" = [] "enable-logs" = ["android_logger"] "tts" = [ "natural-tts" ] "no-unsafe" = ["sxd-document-no-unsafe/no-unsafe", "sxd-xpath-no-unsafe/no-unsafe"] +# Modular feature packaging for lightweight English fork +"lang-en" = [] +"lang-all" = [] +"braille-nemeth" = [] +"braille-ueb" = [] +"braille-all" = [] +"english-minimal" = ["include-zip", "lang-en", "braille-nemeth", "braille-ueb"] + [dependencies] sxd-document-no-unsafe = "0.4.2" diff --git a/src/bin/package_rules.rs b/src/bin/package_rules.rs index 971e95175..4464daa51 100644 --- a/src/bin/package_rules.rs +++ b/src/bin/package_rules.rs @@ -306,4 +306,39 @@ mod tests { assert!(names.contains(&"Rules/Languages/en/en.zip".to_string())); assert!(!names.iter().any(|n| n.contains("/zz"))); } + + #[test] + fn modular_language_and_braille_filtering() { + let tmp = tempdir().unwrap(); + let rules = tmp.path().join("Rules"); + fs::create_dir_all(rules.join("Languages").join("en")).unwrap(); + fs::write(rules.join("Languages").join("en").join("unicode.yaml"), "- b: 2\n").unwrap(); + fs::create_dir_all(rules.join("Languages").join("es")).unwrap(); + fs::write(rules.join("Languages").join("es").join("unicode.yaml"), "- c: 3\n").unwrap(); + fs::create_dir_all(rules.join("Braille").join("Nemeth")).unwrap(); + fs::write(rules.join("Braille").join("Nemeth").join("unicode.yaml"), "- d: 4\n").unwrap(); + fs::create_dir_all(rules.join("Braille").join("French")).unwrap(); + fs::write(rules.join("Braille").join("French").join("unicode.yaml"), "- e: 5\n").unwrap(); + + // Simulate CARGO_FEATURE_LANG_EN and CARGO_FEATURE_BRAILLE_NEMETH + unsafe { + std::env::set_var("CARGO_FEATURE_LANG_EN", "1"); + std::env::set_var("CARGO_FEATURE_BRAILLE_NEMETH", "1"); + } + + let output = tmp.path().join("Rules-filtered.zip"); + package_rules(&rules, &output, false, downloadable_compression()).unwrap(); + let names = outer_names(&fs::read(&output).unwrap()); + + // Clean up env vars + unsafe { + std::env::remove_var("CARGO_FEATURE_LANG_EN"); + std::env::remove_var("CARGO_FEATURE_BRAILLE_NEMETH"); + } + + assert!(names.contains(&"Rules/Languages/en/en.zip".to_string())); + assert!(!names.contains(&"Rules/Languages/es/es.zip".to_string())); + assert!(names.contains(&"Rules/Braille/Nemeth/Nemeth.zip".to_string())); + assert!(!names.contains(&"Rules/Braille/French/French.zip".to_string())); + } } diff --git a/src/rules_archive.rs b/src/rules_archive.rs index 938937683..f564ed961 100644 --- a/src/rules_archive.rs +++ b/src/rules_archive.rs @@ -353,6 +353,24 @@ fn write_inner_zip(source_dir: &Path, inner: SimpleFileOptions) -> io::Result bool { + let has_any_lang_feature = std::env::vars().any(|(k, _)| k.starts_with("CARGO_FEATURE_LANG_") && k != "CARGO_FEATURE_LANG_ALL"); + if !has_any_lang_feature || std::env::var("CARGO_FEATURE_LANG_ALL").is_ok() { + return true; + } + let feature_var = format!("CARGO_FEATURE_LANG_{}", lang.to_uppercase().replace('-', "_")); + return std::env::var(feature_var).is_ok(); +} + +fn is_braille_enabled(braille: &str) -> bool { + let has_any_braille_feature = std::env::vars().any(|(k, _)| k.starts_with("CARGO_FEATURE_BRAILLE_") && k != "CARGO_FEATURE_BRAILLE_ALL"); + if !has_any_braille_feature || std::env::var("CARGO_FEATURE_BRAILLE_ALL").is_ok() { + return true; + } + let feature_var = format!("CARGO_FEATURE_BRAILLE_{}", braille.to_uppercase().replace('-', "_")); + return std::env::var(feature_var).is_ok(); +} + /// Zip each immediate subdirectory of Languages/ or Braille/ into `/.zip`. fn zip_dir( rules_section: &Path, @@ -360,6 +378,8 @@ fn zip_dir( inner: SimpleFileOptions, outer: SimpleFileOptions, archive_prefix: &Path, + is_languages: bool, + is_braille: bool, ) -> io::Result<()> { if !rules_section.is_dir() { return Ok(()); @@ -374,6 +394,12 @@ fn zip_dir( if dir_name == SKIP_LANGUAGE_DIR { continue; } + if is_languages && !is_language_enabled(&dir_name) { + continue; + } + if is_braille && !is_braille_enabled(&dir_name) { + continue; + } if let Some(bytes) = write_inner_zip(&entry_path, inner)? { let zip_path = archive_prefix.join(&dir_name).join(format!("{dir_name}.zip")); add_bytes_to_zip(archive_zip, &zip_path, &bytes, outer)?; @@ -439,6 +465,8 @@ pub fn write_rules_archive( inner, outer, &archive_root.join("Languages"), + true, + false, )?; zip_dir( &rules_dir.join("Braille"), @@ -446,6 +474,8 @@ pub fn write_rules_archive( inner, outer, &archive_root.join("Braille"), + false, + true, )?; archive_zip.finish().map_err(io::Error::other)?;