mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-08 19:42:12 +00:00
proof: a one-flag field keeps its first byte, a directive reads only its first argument
Without FLAG UTF-8, Hunspell reads a field that names one flag (PFX/SFX, KEEPCASE, FORBIDDENWORD and the like) through decode_flag, which takes its first byte: SFX é is the flag 0xC3. Such a field no longer fails the load. SYLLABLENUM lists flags and is read byte by byte like AF. SET and FLAG read only their first argument, so FLAG UTF-8 with a trailing comment keeps Unicode flags.
This commit is contained in:
1 parent
0f961dbc58
commit
e21a078b5a
1 file changed
+32
-3
@@ -228,7 +228,11 @@ pub fn decode(aff: &[u8], dic: &[u8]) -> Result<(String, String), String> {
|
||||
let directive = |name: &str| {
|
||||
head.lines().find_map(|l| {
|
||||
let l = l.trim_start_matches('\u{feff}');
|
||||
l.strip_prefix(name).filter(|rest| rest.starts_with([' ', '\t'])).map(|rest| rest.trim().to_ascii_uppercase())
|
||||
// Only the first argument counts, as in Hunspell: `FLAG UTF-8 # Unicode flags` is UTF-8.
|
||||
l.strip_prefix(name)
|
||||
.filter(|rest| rest.starts_with([' ', '\t']))
|
||||
.and_then(|rest| rest.split([' ', '\t']).find(|f| !f.is_empty()))
|
||||
.map(str::to_ascii_uppercase)
|
||||
})
|
||||
};
|
||||
let set = directive("SET").unwrap_or_else(|| "UTF-8".into());
|
||||
@@ -365,9 +369,12 @@ fn decode_utf8_aff(aff: &[u8]) -> Result<String, String> {
|
||||
let in_comment = comment_at.is_some_and(|c| words.get(c).is_some_and(|&w| i >= w));
|
||||
let decoded = match (dir.as_str(), field) {
|
||||
_ if in_comment => String::from_utf8_lossy(p).into_owned(),
|
||||
("AF" | "PFX" | "SFX" | "COMPOUNDRULE", Some(1)) => bytes_as_flags(p),
|
||||
("AF" | "COMPOUNDRULE" | "SYLLABLENUM", Some(1)) => bytes_as_flags(p),
|
||||
("PFX" | "SFX", Some(3)) | ("CHECKCOMPOUNDPATTERN", Some(1 | 2)) => text_and_flags(p, &what)?,
|
||||
(d, Some(1)) if FLAG_DIRECTIVES.contains(&d) => bytes_as_flags(p),
|
||||
// A field that names one flag keeps its first byte, as Hunspell's `decode_flag`
|
||||
// does: `SFX é` is the flag 0xC3, the first of the two flags `kert/é` carries.
|
||||
("PFX" | "SFX", Some(1)) => bytes_as_flags(p.get(..1).unwrap_or_default()),
|
||||
(d, Some(1)) if FLAG_DIRECTIVES.contains(&d) => bytes_as_flags(p.get(..1).unwrap_or_default()),
|
||||
_ => std::str::from_utf8(p).map_err(|e| format!("{what}: not UTF-8: {e}"))?.to_string(),
|
||||
};
|
||||
out.push_str(&decoded);
|
||||
@@ -501,6 +508,28 @@ mod tests {
|
||||
assert!(decode(b"SET UTF-8\n", b"1\nh\xe1z/a\n").is_err(), "a stem must be UTF-8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_flag_fields_keep_their_first_byte() {
|
||||
// Without `FLAG UTF-8`, a UTF-8 `é` (0xC3 0xA9) in a field that names one flag is the
|
||||
// flag 0xC3; in a `.dic` flag list it is the two flags 0xC3 and 0xA9.
|
||||
let aff = "SET UTF-8\nKEEPCASE é\nSFX é Y 1\nSFX é 0 ok .\nSYLLABLENUM é\n".as_bytes();
|
||||
let (a, d) = decode(aff, "2\nkert/é\nNATO/é\n".as_bytes()).unwrap();
|
||||
assert!(a.contains("KEEPCASE \u{c3}\n") && a.contains("SFX \u{c3} Y 1") && a.contains("SFX \u{c3} 0 ok ."), "{a}");
|
||||
assert!(a.contains("SYLLABLENUM \u{c3}\u{a9}"), "SYLLABLENUM lists flags: {a}");
|
||||
assert!(d.contains("kert/\u{c3}\u{a9}"));
|
||||
let dict = crate::affix::AffixDict::from_hunspell(&a, &d, &[]).unwrap();
|
||||
assert!(dict.check("kert") && dict.check("kertok"));
|
||||
assert!(dict.check("NATO") && !dict.check("Nato"), "KEEPCASE é reaches NATO/é");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_comment_after_flag_utf8_keeps_unicode_flags() {
|
||||
let (_, d) = decode(b"SET UTF-8\nFLAG UTF-8 # Unicode flags\n", "kert/\u{e9}\n".as_bytes()).unwrap();
|
||||
assert_eq!(d, "kert/\u{e9}\n");
|
||||
let (_, d) = decode(b"SET\tUTF-8 # comment\nFLAG\t\tUTF-8\n", "kert/\u{e9}\n".as_bytes()).unwrap();
|
||||
assert_eq!(d, "kert/\u{e9}\n", "repeated spaces and tabs separate the argument");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_russian_single_byte_encodings() {
|
||||
// `ёлка` in KOI8-R (0xA3 0xCC 0xCB 0xC1) and in Windows-1251 (0xB8 0xEB 0xEA 0xE0).
|
||||
|
||||
Reference in new issue
Block a user