From 3ab813dbbe1ef47db05a971986ccf54145ecf752 Mon Sep 17 00:00:00 2001 From: Brady Fomegne Date: Sun, 20 Aug 2023 15:01:19 +0100 Subject: [PATCH] feat(clafrica): implement extension via scripting Now, the Rhai scripting language is supported as embedded scripting language. --- Cargo.lock | 218 ++++++++++++++++++++--- Cargo.toml | 2 +- clafrica/Cargo.toml | 1 + clafrica/data/bad_script.toml | 4 + clafrica/data/bad_script2.toml | 4 + clafrica/data/config_sample.toml | 3 + clafrica/data/scripts/datetime.toml | 4 + clafrica/data/scripts/datetime/core.rhai | 9 + clafrica/data/scripts/datetime/date.rhai | 38 ++++ clafrica/data/scripts/datetime/time.rhai | 3 + clafrica/data/scripts/hi.rhai | 7 + clafrica/data/scripts/invalid.rhai | 4 + clafrica/data/test.toml | 4 +- clafrica/src/config.rs | 79 ++++++++ clafrica/src/lib.rs | 6 +- clafrica/src/translator.rs | 26 ++- 16 files changed, 384 insertions(+), 28 deletions(-) create mode 100644 clafrica/data/bad_script.toml create mode 100644 clafrica/data/bad_script2.toml create mode 100644 clafrica/data/scripts/datetime.toml create mode 100644 clafrica/data/scripts/datetime/core.rhai create mode 100644 clafrica/data/scripts/datetime/date.rhai create mode 100644 clafrica/data/scripts/datetime/time.rhai create mode 100644 clafrica/data/scripts/hi.rhai create mode 100644 clafrica/data/scripts/invalid.rhai diff --git a/Cargo.lock b/Cargo.lock index 3429f36..785e645 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,25 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "const-random", + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bitflags" version = "1.3.2" @@ -14,13 +33,20 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clafrica" -version = "0.3.0" +version = "0.3.1" dependencies = [ "clafrica-lib", "enigo", "rdev", + "rhai", "rstk", "serde", "toml", @@ -45,6 +71,28 @@ dependencies = [ "objc", ] +[[package]] +name = "const-random" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +dependencies = [ + "getrandom", + "once_cell", + "proc-macro-hack", + "tiny-keccak", +] + [[package]] name = "core-foundation" version = "0.7.0" @@ -125,6 +173,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "enigo" version = "0.1.2" @@ -159,6 +213,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.14.0" @@ -175,6 +240,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -202,6 +276,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + [[package]] name = "objc" version = "0.2.7" @@ -223,6 +306,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.66" @@ -234,9 +323,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -257,6 +346,32 @@ dependencies = [ "x11", ] +[[package]] +name = "rhai" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2a11a646ef5d4e4a9d5cf80c7e4ecb20f9b1954292d5c5e6d6cbc8d33728ec" +dependencies = [ + "ahash", + "bitflags", + "instant", + "num-traits", + "rhai_codegen", + "smallvec", + "smartstring", +] + +[[package]] +name = "rhai_codegen" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db74e3fdd29d969a0ec1f8e79171a6f0f71d0429293656901db382d248c4c021" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rstk" version = "0.1.0" @@ -283,7 +398,7 @@ checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.29", ] [[package]] @@ -295,17 +410,60 @@ dependencies = [ "serde", ] +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" -version = "2.0.28" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "toml" version = "0.7.6" @@ -346,6 +504,18 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" @@ -379,9 +549,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -394,51 +564,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.10" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" +checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 036bec4..740361e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,4 @@ members = [ "clafrica-lib", "clafrica" -] \ No newline at end of file +] diff --git a/clafrica/Cargo.toml b/clafrica/Cargo.toml index 4f7fa01..532a17f 100644 --- a/clafrica/Cargo.toml +++ b/clafrica/Cargo.toml @@ -15,6 +15,7 @@ authors = ["Fomegne Brady "] clafrica-lib = { version = "0.3.0", path = "../clafrica-lib" } enigo = "0.1.2" rdev = "0.5.2" +rhai = "1.15.1" serde = { version = "1.0.163", features = ["serde_derive"] } toml = "0.7.3" diff --git a/clafrica/data/bad_script.toml b/clafrica/data/bad_script.toml new file mode 100644 index 0000000..580907b --- /dev/null +++ b/clafrica/data/bad_script.toml @@ -0,0 +1,4 @@ +# script file not found + +[translators] +not_found = "not_found" diff --git a/clafrica/data/bad_script2.toml b/clafrica/data/bad_script2.toml new file mode 100644 index 0000000..417c126 --- /dev/null +++ b/clafrica/data/bad_script2.toml @@ -0,0 +1,4 @@ +# script parsing error + +[translators] +invalid = "./scripts/invalid.rhai" diff --git a/clafrica/data/config_sample.toml b/clafrica/data/config_sample.toml index a2caf45..26cfe2e 100644 --- a/clafrica/data/config_sample.toml +++ b/clafrica/data/config_sample.toml @@ -9,6 +9,9 @@ page_size = 10 [data] sample = { path = "./data_sample.toml" } +[translators] +datetime = { path = "./scripts/datetime.toml" } + [translation] mydict = { path = "./dictionary.toml" } diff --git a/clafrica/data/scripts/datetime.toml b/clafrica/data/scripts/datetime.toml new file mode 100644 index 0000000..765001b --- /dev/null +++ b/clafrica/data/scripts/datetime.toml @@ -0,0 +1,4 @@ +[translators] +date = "./datetime/date.rhai" +time = "./datetime/time.rhai" + diff --git a/clafrica/data/scripts/datetime/core.rhai b/clafrica/data/scripts/datetime/core.rhai new file mode 100644 index 0000000..c9379ce --- /dev/null +++ b/clafrica/data/scripts/datetime/core.rhai @@ -0,0 +1,9 @@ +fn parse_cmd(input) { + let data = input.split('_'); + + if data.len() == 3 { + return data[1]; + } + + return ""; +} diff --git a/clafrica/data/scripts/datetime/date.rhai b/clafrica/data/scripts/datetime/date.rhai new file mode 100644 index 0000000..6a58675 --- /dev/null +++ b/clafrica/data/scripts/datetime/date.rhai @@ -0,0 +1,38 @@ +import `${DIR}/core` as core; + +const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + +fn parse_date(input) { + let data = input.split('/'); + + if data.len() == 3 { + let day = parse_int(data[0]); + let month = parse_int(data[1]); + let year = parse_int(data[2]); + + if day in 1..31 && month in 1..12 && year in 1..2100 { + return [day, month, year]; + } + } + + return []; +} + +// Main function +fn translate(input) { + let data = core::parse_cmd(input); + let date = parse_date(data); + + if !date.is_empty() { + return [input, "", `${date[0]} ${global::MONTHS[date[1]]} ${date[2]}`, true]; + } + + return [input, "", "", false]; +} + +// Test +// print(translate("")); +// print(translate("01/02/2002")); +// print(translate("__")); +// print(translate("_04/06/2010_")); +// print(translate("_04/06/3000_")); diff --git a/clafrica/data/scripts/datetime/time.rhai b/clafrica/data/scripts/datetime/time.rhai new file mode 100644 index 0000000..4d4050d --- /dev/null +++ b/clafrica/data/scripts/datetime/time.rhai @@ -0,0 +1,3 @@ +fn translate(input) { + return ["_time_", "", "", false]; +} diff --git a/clafrica/data/scripts/hi.rhai b/clafrica/data/scripts/hi.rhai new file mode 100644 index 0000000..d69bfe8 --- /dev/null +++ b/clafrica/data/scripts/hi.rhai @@ -0,0 +1,7 @@ +fn translate(input) { + if (input == "hi") { + return ["hi", "", "hello", true]; + } + + return ["hi", "", "", false]; +} diff --git a/clafrica/data/scripts/invalid.rhai b/clafrica/data/scripts/invalid.rhai new file mode 100644 index 0000000..e4bedd7 --- /dev/null +++ b/clafrica/data/scripts/invalid.rhai @@ -0,0 +1,4 @@ +# syntax error + +def hello(): + print("hi") diff --git a/clafrica/data/test.toml b/clafrica/data/test.toml index c3d15ee..77a7c40 100644 --- a/clafrica/data/test.toml +++ b/clafrica/data/test.toml @@ -21,8 +21,10 @@ uu = "ʉ" uu3 = { value = "ʉ̄", alias = ["uu\""] } uuaf3 = { value = "ʉ̄ɑ̄", alias = ["uuqf\""] } +[translators] +hi = "./scripts/hi.rhai" + [translation] -hi = "hello" hello = "hi" heli = "helicopter" hea = "health" diff --git a/clafrica/src/config.rs b/clafrica/src/config.rs index 67d07fa..0a98230 100644 --- a/clafrica/src/config.rs +++ b/clafrica/src/config.rs @@ -1,3 +1,4 @@ +use rhai::{Engine, AST}; use serde::Deserialize; use std::{collections::HashMap, error, fs, path::Path}; use toml::{self}; @@ -6,6 +7,7 @@ use toml::{self}; pub struct Config { pub core: Option, data: Option>, + translators: Option>, translation: Option>, } @@ -71,6 +73,28 @@ impl Config { )?; config.data = Some(data); + // Translators + let mut translators = HashMap::new(); + + config.translators.unwrap_or_default().iter().try_for_each( + |(key, value)| -> Result<(), Box> { + match value { + Data::File(DataFile { path }) => { + let filepath = config_path.join(path); + let conf = Config::from_file(&filepath)?; + translators.extend(conf.translators.unwrap_or_default()); + } + Data::Simple(v) => { + let filepath = config_path.join(v.clone()).to_str().unwrap().to_string(); + translators.insert(key.to_owned(), Data::Simple(filepath)); + } + _ => (), + }; + Ok(()) + }, + )?; + config.translators = Some(translators); + // Translation let mut translation = HashMap::new(); @@ -136,6 +160,38 @@ impl Config { } } + pub fn extract_translators(&self) -> Result, Box> { + let empty = HashMap::default(); + let mut engine = Engine::new(); + + // allow nesting up to 50 layers of expressions/statements + // at global level, but only 10 inside function + engine.set_max_expr_depths(25, 25); + + self.translators + .as_ref() + .unwrap_or(&empty) + .iter() + .filter_map(|(name, filename)| { + let filename = match filename { + Data::Simple(filename) => Some(filename), + _ => None, + }; + + filename.map(|filename| { + let parent = Path::new(&filename).parent().unwrap().to_str().unwrap(); + let header = format!(r#"const DIR = {parent:?};"#); + let ast = engine.compile_file(filename.into()).map_err(|err| { + format!("Failed to parse script file `{filename}`.\nCaused by:\n\t{err}.") + })?; + let ast = engine.compile(header).unwrap().merge(&ast); + + Ok((name.to_owned(), ast)) + }) + }) + .collect() + } + pub fn extract_translation(&self) -> HashMap { let empty = HashMap::new(); @@ -185,6 +241,29 @@ mod tests { assert_eq!(data.keys().len(), 0); } + #[test] + fn from_file_with_translators() { + use crate::config::Config; + use std::path::Path; + + let conf = Config::from_file(Path::new("./data/config_sample.toml")).unwrap(); + let translators = conf.extract_translators().unwrap(); + assert_eq!(translators.keys().len(), 2); + + // translators not provided + let conf = Config::from_file(Path::new("./data/blank_sample.toml")).unwrap(); + let translators = conf.extract_translators().unwrap(); + assert_eq!(translators.keys().len(), 0); + + // scripts parsing error + let conf = Config::from_file(Path::new("./data/bad_script2.toml")).unwrap(); + assert!(conf.extract_translators().is_err()); + + // script file not found + let conf = Config::from_file(Path::new("./data/bad_script.toml")).unwrap(); + assert!(conf.extract_translators().is_err()); + } + #[test] fn from_file_with_translation() { use crate::config::Config; diff --git a/clafrica/src/lib.rs b/clafrica/src/lib.rs index 71dc020..d56a451 100644 --- a/clafrica/src/lib.rs +++ b/clafrica/src/lib.rs @@ -31,6 +31,7 @@ pub fn run( ); let translator = Translator::new( config.extract_translation(), + config.extract_translators()?, config.core.as_ref().map(|e| e.auto_commit).unwrap_or(false), ); let mut is_special_pressed = false; @@ -95,6 +96,7 @@ pub fn run( if let Some(predicate) = frontend.get_selected_predicate() { processor.commit(&predicate.0, &predicate.1, &predicate.2); + frontend.clear_predicates(); } } _ if is_special_pressed => (), @@ -256,8 +258,10 @@ mod tests { // We verify that the translation work as expected input!(KeyH KeyE KeyL KeyL KeyO, typing_speed_ms); output!(textfield, format!("{LIMIT}hi")); + input!(Escape KeyH KeyI, typing_speed_ms); + output!(textfield, format!("{LIMIT}hihello")); - (0..2).for_each(|_| { + (0..7).for_each(|_| { input!(Backspace, typing_speed_ms); }); diff --git a/clafrica/src/translator.rs b/clafrica/src/translator.rs index 8aad84c..429cceb 100644 --- a/clafrica/src/translator.rs +++ b/clafrica/src/translator.rs @@ -1,19 +1,29 @@ +use rhai::{Array, Engine, Scope, AST}; use std::collections::HashMap; pub struct Translator { dictionary: HashMap, + translators: HashMap, auto_commit: bool, } impl Translator { - pub fn new(dictionary: HashMap, auto_commit: bool) -> Self { + pub fn new( + dictionary: HashMap, + translators: HashMap, + auto_commit: bool, + ) -> Self { Self { dictionary, + translators, auto_commit, } } pub fn translate(&self, input: &str) -> Vec<(String, String, String, bool)> { + let mut scope = Scope::new(); + let engine = Engine::new(); + self.dictionary .iter() .filter_map(|(k, v)| { @@ -30,6 +40,20 @@ impl Translator { None } }) + .chain(self.translators.iter().filter_map(|(_name, translator)| { + let data = engine + .call_fn::(&mut scope, translator, "translate", (input.to_owned(),)) + .unwrap_or_default(); + + (data.len() == 4).then(|| { + let code = data[0].clone().into_string().unwrap(); + let remaining_code = data[1].clone().into_string().unwrap(); + let text = data[2].clone().into_string().unwrap(); + let translated = data[3].clone().as_bool().unwrap(); + + (code, remaining_code, text, translated) + }) + })) .collect() } }