mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-08 19:42:12 +00:00
CP1.2: build.rs keys the data tests on a whole data folder, handles non-UTF-8 paths; CI pins craft-i18n
Review of craft-libs#482: - craft_proof_data is set only when every Language data file is embedded, and the new craft_proof_no_data only when none is, so a partial folder (en-us but no pl) runs neither the pattern tests nor the no-data tests. - A CRAFT_I18N_DIR without language-data/ is watched (rerun-if-changed), so the build notices when the checkout gains it. - A CRAFT_I18N_DIR that isn't valid UTF-8 is reported and embeds nothing, instead of a lossy path in include_str!. - proof.yml checks out craft-i18n at 062a6375c322c88b96691a17f7b5fed71c51f7a8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
This commit is contained in:
1 parent
c80b6225f1
commit
48ed6f8cbd
3 files changed
+26
-16
No files matched your search
@@ -25,10 +25,10 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
# The Language data (craft-i18n `language-data/`), embedded by crates/proof/build.rs.
|
||||
# TODO(CP1.2): pin `ref` to the craft-i18n commit that adds language-data/ once it merges.
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: storytold/craft-i18n
|
||||
ref: 062a6375c322c88b96691a17f7b5fed71c51f7a8
|
||||
path: craft-i18n
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable
|
||||
|
||||
+23
-12
@@ -4,12 +4,16 @@
|
||||
//! one constant per file in [`FILES`], each an `include_str!`/`include_bytes!` of
|
||||
//! `$CRAFT_I18N_DIR/language-data/<path>`. Unset, or not a checkout, or a file missing (a warning):
|
||||
//! that constant is empty, and the crate has no embedded Language data for it (English spelling
|
||||
//! falls back to an installed Hunspell dictionary or the crate's own core word list, and
|
||||
//! falls back to an installed Hunspell dictionary or the crate's own copy of the Moby word list, and
|
||||
//! hyphenation that needs the data is off). With `CRAFT_I18N_REQUIRED=1` (releases) any of those
|
||||
//! is an error instead.
|
||||
//!
|
||||
//! When the English word list is embedded, the `craft_proof_data` cfg is set (the tests that pin
|
||||
//! results of the data use it).
|
||||
//! When every file in [`FILES`] is embedded, the `craft_proof_data` cfg is set (the tests that pin
|
||||
//! results of the data use it); when none is, `craft_proof_no_data` (the tests of a build without
|
||||
//! the data). A partial data folder sets neither, so neither set of tests runs against it.
|
||||
//!
|
||||
//! `include_str!` takes a string, so a `CRAFT_I18N_DIR` that isn't valid UTF-8 can't be embedded:
|
||||
//! it is treated like a folder without the data.
|
||||
|
||||
use std::fmt::Write as _;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -32,6 +36,7 @@ fn main() {
|
||||
println!("cargo::rerun-if-env-changed=CRAFT_I18N_DIR");
|
||||
println!("cargo::rerun-if-env-changed=CRAFT_I18N_REQUIRED");
|
||||
println!("cargo::rustc-check-cfg=cfg(craft_proof_data)");
|
||||
println!("cargo::rustc-check-cfg=cfg(craft_proof_no_data)");
|
||||
let required = std::env::var("CRAFT_I18N_REQUIRED").is_ok_and(|v| v.trim() == "1");
|
||||
let level = if required { "error" } else { "warning" };
|
||||
let dir = match std::env::var_os("CRAFT_I18N_DIR").filter(|d| !d.is_empty()) {
|
||||
@@ -47,35 +52,36 @@ fn main() {
|
||||
println!("cargo::error=CRAFT_I18N_REQUIRED=1 but CRAFT_I18N_DIR is unset: a release must embed the Language data");
|
||||
} else {
|
||||
println!(
|
||||
"cargo::warning=CRAFT_I18N_DIR is unset: craft-proof embeds no Language data (English spelling falls back to system Hunspell or the core word list; no hyphenation)"
|
||||
"cargo::warning=CRAFT_I18N_DIR is unset: craft-proof embeds no Language data (English spelling falls back to system Hunspell or the built-in Moby word list; no hyphenation)"
|
||||
);
|
||||
}
|
||||
None
|
||||
}
|
||||
};
|
||||
let mut o = String::from("// Generated by craft-proof's build.rs from CRAFT_I18N_DIR/language-data. Do not edit.\n");
|
||||
let mut english = false;
|
||||
let mut embedded = 0;
|
||||
for &(name, rel, bytes) in FILES {
|
||||
let file = dir.as_ref().map(|d| d.join(rel)).filter(|f| f.is_file());
|
||||
if let (Some(d), None) = (&dir, &file) {
|
||||
println!("cargo::{level}=CRAFT_I18N_DIR: {} is missing; craft-proof embeds none", d.join(rel).display());
|
||||
}
|
||||
let (ty, empty, mac) = if bytes { ("&[u8]", "b\"\"", "include_bytes") } else { ("&str", "\"\"", "include_str") };
|
||||
let value = match &file {
|
||||
// `language_data` only hands back a UTF-8 folder, so this is the path as it is.
|
||||
let value = match file.as_ref().and_then(|f| f.to_str()) {
|
||||
Some(f) => {
|
||||
println!("cargo::rerun-if-changed={}", f.display());
|
||||
if name == "EN_US_DIC" {
|
||||
english = true;
|
||||
}
|
||||
println!("cargo::rerun-if-changed={f}");
|
||||
embedded += 1;
|
||||
// `{:?}` escapes the path (Windows backslashes) as a Rust string literal.
|
||||
format!("{mac}!({:?})", f.display().to_string())
|
||||
format!("{mac}!({f:?})")
|
||||
}
|
||||
None => empty.to_string(),
|
||||
};
|
||||
let _ = writeln!(o, "/// `language-data/{rel}` (empty when it isn't embedded).\npub(crate) static {name}: {ty} = {value};");
|
||||
}
|
||||
if english {
|
||||
if embedded == FILES.len() {
|
||||
println!("cargo::rustc-cfg=craft_proof_data");
|
||||
} else if embedded == 0 {
|
||||
println!("cargo::rustc-cfg=craft_proof_no_data");
|
||||
}
|
||||
let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap_or_default()).join("language_data.rs");
|
||||
if let Err(e) = std::fs::write(&out, o) {
|
||||
@@ -91,8 +97,13 @@ fn language_data(dir: &Path) -> Result<PathBuf, String> {
|
||||
if !dir.is_absolute() {
|
||||
return Err(format!("{} is not an absolute path", dir.display()));
|
||||
}
|
||||
if dir.to_str().is_none() {
|
||||
return Err(format!("{} is not valid UTF-8, which include_str! needs", dir.display()));
|
||||
}
|
||||
let data = dir.join("language-data");
|
||||
if !data.is_dir() {
|
||||
// Notice when the checkout gains its Language data.
|
||||
println!("cargo::rerun-if-changed={}", dir.display());
|
||||
return Err(format!("{} has no language-data/ (a craft-i18n checkout older than its Language data?)", dir.display()));
|
||||
}
|
||||
println!("cargo::rerun-if-changed={}", data.display());
|
||||
|
||||
@@ -1115,9 +1115,8 @@ fn starts_consonant_sound(w: &str) -> bool {
|
||||
#[cfg(test)]
|
||||
pub(crate) static DICTIONARY_TESTS: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
||||
|
||||
/// The no-Language-data build (`build.rs` sets `craft_proof_data` when the English word list is
|
||||
/// embedded).
|
||||
#[cfg(all(test, not(craft_proof_data)))]
|
||||
/// The no-Language-data build (`build.rs` sets `craft_proof_no_data` when nothing is embedded).
|
||||
#[cfg(all(test, craft_proof_no_data))]
|
||||
mod no_data;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in new issue
Block a user