Skip to content

Commit

Permalink
chore: make rhai scripting optional
Browse files Browse the repository at this point in the history
  • Loading branch information
pythonbrad committed Oct 21, 2023
1 parent b8bbb58 commit 807afb0
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- name: build
env:
RUSTFLAGS: -Cinstrument-coverage
run: cargo build --verbose
run: cargo build --all-features --verbose

- name: test
env:
Expand Down
5 changes: 4 additions & 1 deletion config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ readme = "README.md"
authors = ["Brady Fomegne <fomegnemeudje@outlook.com>"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["rhai"]
rhai = ["dep:rhai"]

[dependencies]
rhai = "1.16.2"
rhai = { version = "1.16.2", optional = true }
serde = { version = "1.0.188", features = ["derive"] }
toml = "0.8.2"
53 changes: 30 additions & 23 deletions config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#![deny(missing_docs)]

#[cfg(feature = "rhai")]
use rhai::{Engine, AST};
use serde::Deserialize;
use std::result::Result;
Expand All @@ -15,6 +16,7 @@ pub struct Config {
/// The core config.
pub core: Option<CoreConfig>,
data: Option<HashMap<String, Data>>,
#[cfg(feature = "rhai")]
translators: Option<HashMap<String, Data>>,
translation: Option<HashMap<String, Data>>,
}
Expand Down Expand Up @@ -113,26 +115,29 @@ 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<dyn error::Error>> {
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(value) => {
let filepath = config_path.join(value.clone()).to_str().unwrap().to_string();
translators.insert(key.to_owned(), Data::Simple(filepath));
}
_ => Err(format!("Invalid script file `{filepath:?}`.\nCaused by:\n\t{value:?} not allowed in the translator table."))?,
};
Ok(())
},
)?;
config.translators = Some(translators);
#[cfg(feature = "rhai")]
{
let mut translators = HashMap::new();

config.translators.unwrap_or_default().iter().try_for_each(
|(key, value)| -> Result<(), Box<dyn error::Error>> {
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(value) => {
let filepath = config_path.join(value.clone()).to_str().unwrap().to_string();
translators.insert(key.to_owned(), Data::Simple(filepath));
}
_ => Err(format!("Invalid script file `{filepath:?}`.\nCaused by:\n\t{value:?} not allowed in the translator table."))?,
};
Ok(())
},
)?;
config.translators = Some(translators);
}

// Translation
let mut translation = HashMap::new();
Expand Down Expand Up @@ -187,6 +192,7 @@ impl Config {
}

/// Extract the translators from the configuration.
#[cfg(feature = "rhai")]
pub fn extract_translators(&self) -> Result<HashMap<String, AST>, Box<dyn error::Error>> {
let empty = HashMap::default();
let mut engine = Engine::new();
Expand Down Expand Up @@ -282,14 +288,15 @@ mod tests {
// invalid data
let conf = Config::from_file(Path::new("./data/invalid_data.toml"));
assert!(conf.is_err());
}

#[cfg(feature = "rhai")]
#[test]
fn from_file_with_translators() {
// invalid translator
let conf = Config::from_file(Path::new("./data/invalid_translator.toml"));
assert!(conf.is_err());
}

#[test]
fn from_file_with_translators() {
let conf = Config::from_file(Path::new("./data/config_sample.toml")).unwrap();
let translators = conf.extract_translators().unwrap();
assert_eq!(translators.keys().len(), 2);
Expand Down
6 changes: 5 additions & 1 deletion engine/translator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ authors = ["Brady Fomegne <fomegnemeudje@outlook.com>"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["rhai"]
rhai = ["dep:rhai"]

[dependencies]
rhai = "1.16.2"
rhai = { version = "1.16.2", optional = true }
141 changes: 85 additions & 56 deletions engine/translator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,39 @@
//!
//! Example
//! ```rust
//! use clafrica_translator::{Engine, Translator};
//! #[cfg(feature = "rhai")]
//! use clafrica_translator::Engine;
//! use clafrica_translator::Translator;
//! use std::collections::HashMap;
//!
//! // Translation via scripting
//! let engine = Engine::new();
//! let hi = engine.compile(r#"
//! fn translate(input) {
//! if input == "hi" {
//! ["hi", "", "hello", true]
//! }
//! }
//! "#).unwrap();
//! let mut translators = HashMap::new();
//! translators.insert("hi".to_string(), hi);
//!
//! // Translation via dictionary
//! let mut dictionary = HashMap::new();
//! dictionary.insert("halo".to_string(), ["hello".to_string()].to_vec());
//! dictionary.insert("nihao".to_string(), ["hello".to_string()].to_vec());
//!
//! // Translation via scripting
//! #[cfg(feature = "rhai")]
//! let mut translators = HashMap::new();
//! #[cfg(feature = "rhai")]
//! {
//! let engine = Engine::new();
//! let hi = engine.compile(r#"
//! fn translate(input) {
//! if input == "hi" {
//! ["hi", "", "hello", true]
//! }
//! }
//! "#).unwrap();
//! translators.insert("hi".to_string(), hi);
//! }
//!
//! // We build the translator.
//! #[cfg(not(feature = "rhai"))]
//! let translator = Translator::new(dictionary, true);
//! #[cfg(feature = "rhai")]
//! let translator = Translator::new(dictionary, translators, true);
//!
//! #[cfg(feature = "rhai")]
//! assert_eq!(
//! translator.translate("hi"),
//! vec![(
Expand All @@ -39,13 +49,16 @@

#![deny(missing_docs)]

#[cfg(feature = "rhai")]
pub use rhai::Engine;
#[cfg(feature = "rhai")]
use rhai::{Array, Scope, AST};
use std::collections::HashMap;

/// Core structure of the translator.
pub struct Translator {
dictionary: HashMap<String, Vec<String>>,
#[cfg(feature = "rhai")]
translators: HashMap<String, AST>,
auto_commit: bool,
}
Expand All @@ -54,43 +67,46 @@ impl Translator {
/// Initiate a new translator.
pub fn new(
dictionary: HashMap<String, Vec<String>>,
translators: HashMap<String, AST>,
#[cfg(feature = "rhai")] translators: HashMap<String, AST>,
auto_commit: bool,
) -> Self {
Self {
dictionary,
#[cfg(feature = "rhai")]
translators,
auto_commit,
}
}

/// Generate a list of predicates based on the input.
pub fn translate(&self, input: &str) -> Vec<(String, String, Vec<String>, bool)> {
#[cfg(feature = "rhai")]
let mut scope = Scope::new();
#[cfg(feature = "rhai")]
let engine = Engine::new();

self.dictionary
.iter()
.filter_map(|(key, value)| {
if key == input {
Some((
key.to_owned(),
"".to_owned(),
value.to_owned(),
self.auto_commit,
))
} else if input.len() > 1 && key.starts_with(input) {
Some((
key.to_owned(),
key.chars().skip(input.len()).collect(),
value.to_owned(),
false,
))
} else {
None
}
})
.chain(self.translators.iter().filter_map(|(_name, translator)| {
let predicates = self.dictionary.iter().filter_map(|(key, value)| {
if key == input {
Some((
key.to_owned(),
"".to_owned(),
value.to_owned(),
self.auto_commit,
))
} else if input.len() > 1 && key.starts_with(input) {
Some((
key.to_owned(),
key.chars().skip(input.len()).collect(),
value.to_owned(),
false,
))
} else {
None
}
});
#[cfg(feature = "rhai")]
let predicates =
predicates.chain(self.translators.iter().filter_map(|(_name, translator)| {
let data = engine
.call_fn::<Array>(&mut scope, translator, "translate", (input.to_owned(),))
.unwrap_or_default();
Expand All @@ -109,41 +125,54 @@ impl Translator {

(code, remaining_code, texts, translated)
})
}))
.collect()
}));
predicates.collect()
}
}

#[cfg(test)]
mod tests {
#[test]
fn test_translate() {
use crate::{Engine, Translator};
#[cfg(feature = "rhai")]
use crate::Engine;
use crate::Translator;
use std::collections::HashMap;

let engine = Engine::new();
let ast1 = engine.compile("fn translate(input) {}").unwrap();
let ast2 = engine
.compile(
r#"
fn translate(input) {
if input == "hi" {
["hi", "", "hello", true]
}
}
"#,
)
.unwrap();
let mut translators = HashMap::new();
translators.insert("none".to_string(), ast1);
translators.insert("some".to_string(), ast2);

// We build the translation
let mut dictionary = HashMap::new();
dictionary.insert("halo".to_string(), ["hello".to_string()].to_vec());

//
#[cfg(feature = "rhai")]
let mut translators = HashMap::new();
#[cfg(feature = "rhai")]
{
let engine = Engine::new();
let ast1 = engine.compile("fn translate(input) {}").unwrap();
let ast2 = engine
.compile(
r#"
fn translate(input) {
if input == "hi" {
["hi", "", "hello", true]
}
}
"#,
)
.unwrap();
translators.insert("none".to_string(), ast1);
translators.insert("some".to_string(), ast2);
}

// We config the translator
#[cfg(not(feature = "rhai"))]
let translator = Translator::new(dictionary, true);
#[cfg(feature = "rhai")]
let translator = Translator::new(dictionary, translators, true);

assert_eq!(translator.translate("h"), vec![]);
#[cfg(feature = "rhai")]
assert_eq!(
translator.translate("hi"),
vec![(
Expand Down
8 changes: 6 additions & 2 deletions service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ doc = false
name = "clafrica"
path = "./src/main.rs"

[features]
default = ["rhai"]
rhai = ["clafrica-config/rhai", "clafrica-translator/rhai"]

[dependencies]
clap = { version = "4.4.6", features = ["derive"] }
enigo = "0.1.3"
clafrica-config = { version = "0.4.1", path = "../config" }
clafrica-config = { version = "0.4.1", path = "../config", default-features = false }
clafrica-preprocessor = { version = "0.5.0", path = "../engine/preprocessor" }
clafrica-translator = { version = "0.0.1", path = "../engine/translator" }
clafrica-translator = { version = "0.0.1", path = "../engine/translator", default-features = false }
rdev = "0.5.3"

[dev-dependencies]
Expand Down
4 changes: 4 additions & 0 deletions service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub fn run(config: Config, mut frontend: impl Frontend) -> Result<(), Box<dyn er
let mut preprocessor = Preprocessor::new(map, buffer_size);
let translator = Translator::new(
config.extract_translation(),
#[cfg(feature = "rhai")]
config.extract_translators()?,
auto_commit,
);
Expand Down Expand Up @@ -278,6 +279,9 @@ mod tests {
// We verify that the translation work as expected
input!(KeyH KeyE KeyL KeyL KeyO, typing_speed_ms);
output!(textfield, format!("{LIMIT}uuɑαⱭⱭɑɑhi"));
#[cfg(not(feature = "rhai"))]
input!(Escape KeyH Escape KeyE KeyL KeyL KeyO, typing_speed_ms);
#[cfg(feature = "rhai")]
input!(Escape KeyH KeyI, typing_speed_ms);
output!(textfield, format!("{LIMIT}uuɑαⱭⱭɑɑhihello"));
input!(Escape, typing_speed_ms);
Expand Down

0 comments on commit 807afb0

Please sign in to comment.