diff --git a/CHANGELOG.md b/CHANGELOG.md index e57d781..f4e6313 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,14 @@ This project follows [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Added +- `Checker::is_correct` + ### Changed - Windows backend now uses the official `windows` crate instead of `winapi` - Bump `cfg-if` to 1.x - macOS backend now uses `objc2` / `objc2-app-kit` instead of `cocoa` / `objc` +- `Checker::check` now takes `&self` instead of `&mut self` ### Fixed - Pass null-terminated dictionary paths to Hunspell on Linux (stops spurious diff --git a/Cargo.lock b/Cargo.lock index be3f4ad..d104488 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 1.0.109", "which", ] @@ -37,10 +37,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] -name = "block" -version = "0.1.6" +name = "block2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] [[package]] name = "cexpr" @@ -75,46 +78,13 @@ dependencies = [ ] [[package]] -name = "cocoa" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4736c86d51bd878b474400d9ec888156f4037015f5d09794fab9f26eab1ad4" -dependencies = [ - "bitflags 1.0.4", - "block", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" - -[[package]] -name = "core-graphics" -version = "0.19.0" +name = "dispatch2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e78b2e0aaf43f08e7ae0d6bc96895ef72ff0921c7d4ff4762201b2dba376dd" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 1.0.4", - "core-foundation", - "foreign-types", - "libc", + "bitflags 2.13.1", + "objc2", ] [[package]] @@ -133,21 +103,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "glob" version = "0.3.0" @@ -216,15 +171,6 @@ dependencies = [ "cfg-if 0.1.7", ] -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.3.0" @@ -248,12 +194,155 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.6" +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.1", + "block2", + "libc", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-text", + "objc2-core-video", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.1", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.1", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "malloc_buf", + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.1", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-foundation", ] [[package]] @@ -337,10 +426,11 @@ name = "spellbound" version = "0.2.0" dependencies = [ "cfg-if 1.0.4", - "cocoa", "hunspell-sys", - "objc", - "winapi", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "windows", ] [[package]] @@ -354,6 +444,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "unicode-ident" version = "1.0.24" @@ -373,26 +474,71 @@ dependencies = [ ] [[package]] -name = "winapi" -version = "0.3.7" +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "windows-future" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] [[package]] name = "windows-link" @@ -400,6 +546,34 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -434,6 +608,15 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" diff --git a/src/lib.rs b/src/lib.rs index ffa995c..bd8eaec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,13 +96,15 @@ impl Checker { } /// Check a text for spelling errors. Returns an iterator over the errors present in the text. - pub fn check<'a, 'b: 'a>( - &'b mut self, - text: &'a str, - ) -> impl Iterator + 'a { + pub fn check<'a>(&self, text: &'a str) -> impl Iterator + 'a { self.0.check(text).map(SpellingError) } + /// Returns true if `word` is spelled correctly. + pub fn is_correct(&self, word: &str) -> bool { + self.check(word).next().is_none() + } + /// Instructs the spell checker to ignore a word in future checks. The word is temporarily /// added to the spell checker's ignore list, and other instances of the spell checker will not /// ignore the word. @@ -134,14 +136,14 @@ mod tests { #[test] fn no_errors() { let text = "I'm happy that this sentence has no errors."; - let mut checker = Checker::new().unwrap(); + let checker = Checker::new().unwrap(); assert_eq!(checker.check(text).count(), 0); } #[test] fn single_error() { let text = "beleeve"; - let mut checker = Checker::new().unwrap(); + let checker = Checker::new().unwrap(); let errors = checker.check(text).collect::>(); assert_eq!(errors.len(), 1); assert_eq!(errors[0].text(), "beleeve"); @@ -151,7 +153,7 @@ mod tests { #[test] fn multiple_errors() { let text = "asdf hjkl qwer uiop"; - let mut checker = Checker::new().unwrap(); + let checker = Checker::new().unwrap(); let errors = checker.check(text).collect::>(); assert_eq!(errors.len(), 4); assert_eq!(errors[0].text(), "asdf"); @@ -163,7 +165,7 @@ mod tests { #[test] fn error_ranges() { let text = "one asdf two"; - let mut checker = Checker::new().unwrap(); + let checker = Checker::new().unwrap(); let errors: Vec<_> = checker.check(text).collect(); assert_eq!(errors.len(), 1); assert_eq!(errors[0].text(), "asdf"); @@ -174,31 +176,24 @@ mod tests { #[test] fn empty() { - let mut checker = Checker::new().unwrap(); + let checker = Checker::new().unwrap(); assert_eq!(checker.check("").count(), 0); } #[test] fn ignore() { let mut checker = Checker::new().unwrap(); - assert_eq!(checker.check("foobarbaz").count(), 1); - checker.ignore("foobarbaz"); - assert_eq!(checker.check("foobarbaz").count(), 0); } #[test] fn ignore_not_permanent() { let mut checker = Checker::new().unwrap(); - checker.ignore("foobarbaz"); - drop(checker); - - let mut checker = Checker::new().unwrap(); - + let checker = Checker::new().unwrap(); assert_eq!(checker.check("foobarbaz").count(), 1); } @@ -220,4 +215,11 @@ mod tests { let suggestions = checker.suggest("beleeve"); assert!(!suggestions.is_empty()); } + + #[test] + fn is_correct() { + let checker = Checker::new().unwrap(); + assert!(checker.is_correct("believe")); + assert!(!checker.is_correct("beleeve")); + } } diff --git a/src/mac.rs b/src/mac.rs index adf03fe..ae85770 100644 --- a/src/mac.rs +++ b/src/mac.rs @@ -22,7 +22,10 @@ fn language_ref(language: &Option) -> Option> { language.as_ref().map(|tag| ns_string(tag)) } -fn nsarray_to_strings(array: Option<&objc2_foundation::NSArray>, max: usize) -> Vec { +fn nsarray_to_strings( + array: Option<&objc2_foundation::NSArray>, + max: usize, +) -> Vec { let Some(array) = array else { return Vec::new(); }; @@ -107,7 +110,7 @@ impl Checker { with_checker(|c| c.ignoreWord_inSpellDocumentWithTag(&ns_word, tag)); } - pub fn check(&mut self, text: &str) -> impl Iterator { + pub fn check(&self, text: &str) -> impl Iterator { SpellcheckIter { document_tag: self.document_tag, ns_text: ns_string(text), diff --git a/src/main.rs b/src/main.rs index f4c9e32..87946ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use spellbound::Checker; fn main() { let text = env::args().skip(1).collect::>().join(" "); - let mut checker = Checker::new().unwrap(); + let checker = Checker::new().unwrap(); for error in checker.check(&text) { println!("ERROR: {}", error.text()); diff --git a/src/unix.rs b/src/unix.rs index 908fc74..385eac6 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -95,10 +95,7 @@ impl Checker { } } - pub fn check<'a, 'b: 'a>( - &'b mut self, - text: &'a str, - ) -> impl Iterator + 'a { + pub fn check<'a>(&self, text: &'a str) -> impl Iterator + 'a { let hunspell = self.hunspell; words(text).filter_map(move |(start, end, word)| { diff --git a/src/win.rs b/src/win.rs index 6d88dba..5ad9ebd 100644 --- a/src/win.rs +++ b/src/win.rs @@ -38,10 +38,9 @@ fn open_for_language(bcp47: &str) -> Result { // S_OK / S_FALSE (already initialized) both succeed via windows::Result let _ = unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }; - let factory: ISpellCheckerFactory = unsafe { - CoCreateInstance(&SpellCheckerFactory, None, CLSCTX_INPROC_SERVER) - } - .map_err(|_| Error::Unavailable)?; + let factory: ISpellCheckerFactory = + unsafe { CoCreateInstance(&SpellCheckerFactory, None, CLSCTX_INPROC_SERVER) } + .map_err(|_| Error::Unavailable)?; let tag = HSTRING::from(bcp47); unsafe { factory.CreateSpellChecker(&tag) }.map_err(|_| Error::Unavailable) @@ -95,7 +94,7 @@ impl Checker { out } - pub fn check(&mut self, text: &str) -> impl Iterator { + pub fn check(&self, text: &str) -> impl Iterator { if text.is_empty() { return ErrorIter { original: String::new(),