diff --git a/examples/event_handler_js/main.rs b/examples/event_handler_js/main.rs new file mode 100644 index 00000000..e47233d6 --- /dev/null +++ b/examples/event_handler_js/main.rs @@ -0,0 +1,153 @@ +//! Implementation of the Event Handler With State Pattern - JS Style +use rhai::{Dynamic, Engine, Map, Scope, AST}; + +use std::io::{stdin, stdout, Write}; + +const SCRIPT_FILE: &str = "event_handler_js/script.rhai"; + +#[derive(Debug)] +struct Handler { + pub engine: Engine, + pub scope: Scope<'static>, + pub states: Dynamic, + pub ast: AST, +} + +fn print_scope(scope: &Scope) { + scope + .iter_raw() + .enumerate() + .for_each(|(i, (name, constant, value))| { + println!( + "[{}] {}{}{} = {:?}", + i + 1, + if constant { "const " } else { "" }, + name, + if value.is_shared() { " (shared)" } else { "" }, + *value.read_lock::().unwrap(), + ) + }); + println!(); +} + +pub fn main() { + println!("Events Handler Example - JS Style"); + println!("=================================="); + + let mut input = String::new(); + + // Read script file + print!("Script file [{}]: ", SCRIPT_FILE); + stdout().flush().expect("flush stdout"); + + input.clear(); + + stdin().read_line(&mut input).expect("read input"); + + let path = match input.trim() { + "" => SCRIPT_FILE, + path => path, + }; + + // Create Engine + let engine = Engine::new(); + + // Use an object map to hold state + let mut states = Map::new(); + + // Default states can be added + states.insert("bool_state".into(), Dynamic::FALSE); + + // Convert the object map into 'Dynamic' + let mut states: Dynamic = states.into(); + + // Create a custom 'Scope' to hold state + let mut scope = Scope::new(); + + // Add any system-provided state into the custom 'Scope'. + // Constants can be used to optimize the script. + scope.push_constant("MY_CONSTANT", 42_i64); + + // Compile the handler script. + println!("> Loading script file: {}", path); + + let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { + Ok(ast) => ast, + Err(err) => { + eprintln!("! Error: {}", err); + println!("Cannot continue. Bye!"); + return; + } + }; + + println!("> Script file loaded."); + println!(); + println!("quit = exit program"); + println!("scope = print scope"); + println!("states = print states"); + println!("event arg = run function with argument"); + println!(); + + // Run the 'init' function to initialize the state, retaining variables. + let result = engine.call_fn_raw(&mut scope, &ast, false, true, "init", Some(&mut states), []); + + if let Err(err) = result { + eprintln!("! {}", err) + } + + // Create handler instance + let mut handler = Handler { + engine, + scope, + states, + ast, + }; + + // Events loop + loop { + print!("event> "); + stdout().flush().expect("flush stdout"); + + // Read event + input.clear(); + stdin().read_line(&mut input).expect("read input"); + + let mut fields = input.trim().splitn(2, ' '); + + let event = fields.next().expect("event").trim(); + let arg = fields.next().unwrap_or(""); + + // Process event + match event { + "quit" => break, + + "scope" => { + print_scope(&handler.scope); + continue; + } + + "states" => { + println!("{:?}", handler.states); + println!(); + continue; + } + + // Map all other events to function calls + _ => { + let engine = &handler.engine; + let scope = &mut handler.scope; + let ast = &handler.ast; + let this_ptr = Some(&mut handler.states); + + let result = + engine.call_fn_raw(scope, ast, false, true, event, this_ptr, [arg.into()]); + + if let Err(err) = result { + eprintln!("! {}", err) + } + } + } + } + + println!("Bye!"); +} diff --git a/examples/event_handler_js/script.rhai b/examples/event_handler_js/script.rhai new file mode 100644 index 00000000..7c488515 --- /dev/null +++ b/examples/event_handler_js/script.rhai @@ -0,0 +1,49 @@ +// Implementation of the Event Handler With State Pattern - JS Style + +/// Initialize user-provided state. +fn init() { + // Can detect system-provided default states! + // Add 'bool_state' as new state variable if one does not exist + if !("bool_state" in this) { + this.bool_state = false; + } + // Add 'value' as new state variable (overwrites any existing) + this.value = 0; + + // Can also add OOP-style functions! + this.log = |x| print(`State = ${this.value}, data = ${x}`); +} + +/// 'start' event handler +fn start(data) { + if this.bool_state { + throw "Already started!"; + } + if this.value <= 0 { + throw "Conditions not yet ready to start!"; + } + this.bool_state = true; + this.value += parse_int(data); + + // Constant 'MY_CONSTANT' in custom scope is also visible! + print(`MY_CONSTANT = ${MY_CONSTANT}`); +} + +/// 'end' event handler +fn end(data) { + if !this.bool_state { + throw "Not yet started!"; + } + if this.value > 0 { + throw "Conditions not yet ready to end!"; + } + this.bool_state = false; + this.value = parse_int(data); +} + +/// 'update' event handler +fn update(data) { + let data = parse_int(data); + this.value += data; + this.log(data); +} diff --git a/examples/event_handler_main/main.rs b/examples/event_handler_main/main.rs new file mode 100644 index 00000000..5fddb1af --- /dev/null +++ b/examples/event_handler_main/main.rs @@ -0,0 +1,129 @@ +//! Implementation of the Event Handler With State Pattern - Main Style +use rhai::{Dynamic, Engine, Scope, AST}; + +use std::io::{stdin, stdout, Write}; + +const SCRIPT_FILE: &str = "event_handler_main/script.rhai"; + +#[derive(Debug)] +struct Handler { + pub engine: Engine, + pub scope: Scope<'static>, + pub ast: AST, +} + +fn print_scope(scope: &Scope) { + scope + .iter_raw() + .enumerate() + .for_each(|(i, (name, constant, value))| { + println!( + "[{}] {}{}{} = {:?}", + i + 1, + if constant { "const " } else { "" }, + name, + if value.is_shared() { " (shared)" } else { "" }, + *value.read_lock::().unwrap(), + ) + }); + println!(); +} + +pub fn main() { + println!("Events Handler Example - Main Style"); + println!("==================================="); + + let mut input = String::new(); + + // Read script file + print!("Script file [{}]: ", SCRIPT_FILE); + stdout().flush().expect("flush stdout"); + + input.clear(); + + stdin().read_line(&mut input).expect("read input"); + + let path = match input.trim() { + "" => SCRIPT_FILE, + path => path, + }; + + // Create Engine + let engine = Engine::new(); + + // Create a custom 'Scope' to hold state + let mut scope = Scope::new(); + + // Add any system-provided state into the custom 'Scope'. + // Constants can be used to optimize the script. + scope.push_constant("MY_CONSTANT", 42_i64); + + // Compile the handler script. + println!("> Loading script file: {}", path); + + let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { + Ok(ast) => ast, + Err(err) => { + eprintln!("! Error: {}", err); + println!("Cannot continue. Bye!"); + return; + } + }; + + println!("> Script file loaded."); + println!(); + println!("quit = exit program"); + println!("scope = print scope"); + println!("event arg = run function with argument"); + println!(); + + // Run the 'init' function to initialize the state, retaining variables. + let result = engine.call_fn_raw(&mut scope, &ast, false, false, "init", None, []); + + if let Err(err) = result { + eprintln!("! {}", err) + } + + // Create handler instance + let mut handler = Handler { engine, scope, ast }; + + // Events loop + loop { + print!("event> "); + stdout().flush().expect("flush stdout"); + + // Read event + input.clear(); + stdin().read_line(&mut input).expect("read input"); + + let mut fields = input.trim().splitn(2, ' '); + + let event = fields.next().expect("event").trim(); + let arg = fields.next().unwrap_or(""); + + // Process event + match event { + "quit" => break, + + "scope" => { + print_scope(&handler.scope); + continue; + } + + // Map all other events to function calls + _ => { + let engine = &handler.engine; + let scope = &mut handler.scope; + let ast = &handler.ast; + + let result: Result<(), _> = engine.call_fn(scope, ast, event, (arg.to_string(),)); + + if let Err(err) = result { + eprintln!("! {}", err) + } + } + } + } + + println!("Bye!"); +} diff --git a/examples/event_handler_main/script.rhai b/examples/event_handler_main/script.rhai new file mode 100644 index 00000000..641c2372 --- /dev/null +++ b/examples/event_handler_main/script.rhai @@ -0,0 +1,56 @@ +// Implementation of the Event Handler With State Pattern - Main Style + +/// Initialize user-provided state (shadows system-provided state, if any). +fn init() { + // Add 'bool_state' and 'obj_state' as new state variables + let bool_state = false; + let value = 0; + + // Constants can also be added! + const EXTRA_CONSTANT = "hello, world!"; +} + +/// Without 'OOP' support, the can only be a function. +fn log(value, data) { + print(`State = ${value}, data = ${data}`); +} + +/// 'start' event handler +fn start(data) { + if bool_state { + throw "Already started!"; + } + if value <= 0 { + throw "Conditions not yet ready to start!"; + } + bool_state = true; + + // Constants 'MY_CONSTANT' and 'EXTRA_CONSTANT' + // in custom scope are also visible! + print(`MY_CONSTANT = ${MY_CONSTANT}`); + print(`EXTRA_CONSTANT = ${EXTRA_CONSTANT}`); + + value += parse_int(data); +} + +/// 'end' event handler +fn end(data) { + if !bool_state { + throw "Not yet started!"; + } + if value > 0 { + throw "Conditions not yet ready to end!"; + } + bool_state = false; + value = parse_int(data); +} + +/// 'update' event handler +fn update(data) { + let data = parse_int(data); + + value += data; + + // Without OOP support, can only call function + log(value, data); +} diff --git a/examples/event_handler_map/main.rs b/examples/event_handler_map/main.rs new file mode 100644 index 00000000..5a6c8aa1 --- /dev/null +++ b/examples/event_handler_map/main.rs @@ -0,0 +1,138 @@ +//! Implementation of the Event Handler With State Pattern - Map Style +use rhai::{Dynamic, Engine, Map, Scope, AST}; + +use std::io::{stdin, stdout, Write}; + +const SCRIPT_FILE: &str = "event_handler_map/script.rhai"; + +#[derive(Debug)] +struct Handler { + pub engine: Engine, + pub scope: Scope<'static>, + pub ast: AST, +} + +fn print_scope(scope: &Scope) { + scope + .iter_raw() + .enumerate() + .for_each(|(i, (name, constant, value))| { + println!( + "[{}] {}{}{} = {:?}", + i + 1, + if constant { "const " } else { "" }, + name, + if value.is_shared() { " (shared)" } else { "" }, + *value.read_lock::().unwrap(), + ) + }); + println!(); +} + +pub fn main() { + println!("Events Handler Example - Map Style"); + println!("=================================="); + + let mut input = String::new(); + + // Read script file + print!("Script file [{}]: ", SCRIPT_FILE); + stdout().flush().expect("flush stdout"); + + input.clear(); + + stdin().read_line(&mut input).expect("read input"); + + let path = match input.trim() { + "" => SCRIPT_FILE, + path => path, + }; + + // Create Engine + let engine = Engine::new(); + + // Create a custom 'Scope' to hold state + let mut scope = Scope::new(); + + // Add any system-provided state into the custom 'Scope'. + // Constants can be used to optimize the script. + scope.push_constant("MY_CONSTANT", 42_i64); + + // Use an object map to hold state + let mut states = Map::new(); + + // Default states can be added + states.insert("bool_state".into(), Dynamic::FALSE); + + // Add the main states-holding object map and call it 'state' + scope.push("state", Map::new()); + + // Compile the handler script. + println!("> Loading script file: {}", path); + + let ast = match engine.compile_file_with_scope(&mut scope, path.into()) { + Ok(ast) => ast, + Err(err) => { + eprintln!("! Error: {}", err); + println!("Cannot continue. Bye!"); + return; + } + }; + + println!("> Script file loaded."); + println!(); + println!("quit = exit program"); + println!("scope = print scope"); + println!("event arg = run function with argument"); + println!(); + + // Run the 'init' function to initialize the state, retaining variables. + let result: Result<(), _> = engine.call_fn(&mut scope, &ast, "init", ()); + + if let Err(err) = result { + eprintln!("! {}", err) + } + + // Create handler instance + let mut handler = Handler { engine, scope, ast }; + + // Events loop + loop { + print!("event> "); + stdout().flush().expect("flush stdout"); + + // Read event + input.clear(); + stdin().read_line(&mut input).expect("read input"); + + let mut fields = input.trim().splitn(2, ' '); + + let event = fields.next().expect("event").trim(); + let arg = fields.next().unwrap_or(""); + + // Process event + match event { + "quit" => break, + + "scope" => { + print_scope(&handler.scope); + continue; + } + + // Map all other events to function calls + _ => { + let engine = &handler.engine; + let scope = &mut handler.scope; + let ast = &handler.ast; + + let result: Result<(), _> = engine.call_fn(scope, ast, event, (arg.to_string(),)); + + if let Err(err) = result { + eprintln!("! {}", err) + } + } + } + } + + println!("Bye!"); +} diff --git a/examples/event_handler_map/script.rhai b/examples/event_handler_map/script.rhai new file mode 100644 index 00000000..988ca170 --- /dev/null +++ b/examples/event_handler_map/script.rhai @@ -0,0 +1,57 @@ +// Implementation of the Event Handler With State Pattern - Map Style + +/// Initialize user-provided state. +/// State is stored inside an object map bound to 'state'. +fn init() { + // Add 'bool_state' as new state variable if one does not exist + if !("bool_state" in state) { + state.bool_state = false; + } + // Add 'obj_state' as new state variable (overwrites any existing) + state.value = 0; + + // Can also add OOP-style functions! + state.log = |x| print(`State = ${this.value}, data = ${x}`); +} + +/// 'start' event handler +fn start(data) { + // Can detect system-provided default states! + // Access state variables in 'state' + if state.bool_state { + throw "Already started!"; + } + + // New values can be added to the state + state.start_mode = data; + + if state.value <= 0 { + throw "Conditions not yet ready to start!"; + } + state.bool_state = true; + state.value = parse_int(data); + + // Constant 'MY_CONSTANT' in custom scope is also visible! + print(`MY_CONSTANT = ${MY_CONSTANT}`); +} + +/// 'end' event handler +fn end(data) { + if !state.bool_state || !("start_mode" in state) { + throw "Not yet started!"; + } + if state.value > 0 { + throw "Conditions not yet ready to end!"; + } + state.bool_state = false; + state.value = parse_int(data); +} + +/// 'update' event handler +fn update(data) { + let data = parse_int(data); + state.value += data; + + // Call user-defined function OOP-style! + state.log(data); +}