use crate::*; /// A collection of input bindings. /// /// Each contained layer defines a mapping from input event to command invocation /// over a given state. Furthermore, each layer may have an associated condition, /// so that only certain layers are active at a given time depending on state. #[derive(Debug)] pub struct InputLayers(Vec>); /// A single input binding layer. #[derive(Default, Debug)] struct InputLayer { condition: Option>, bindings: DslVal, } /// Input layers start out empty regardless if `T` implements [Default]. impl Default for InputLayers { fn default () -> Self { Self(vec![]) } } /// Bring up an input layer map from a string representation. impl<'s> From<&'s str> for InputLayers { fn from (source: &'s str) -> Self { let mut layers = vec![]; let mut source = CstIter::from(source); while let Some(cst) = source.next() { panic!("{:?}", cst.value); layers.push(match cst.value.exp_head().map(|x|x.as_key()).flatten() { Some("layer") => InputLayer { condition: None, bindings: dsl_val(source.nth(1).unwrap()), }, Some("layer-if") => InputLayer { condition: Some(dsl_val(source.nth(1).unwrap())), bindings: dsl_val(source.nth(2).unwrap()), }, _ => panic!("this shoulda been a TryFrom"), }); } Self(layers) } } impl InputLayers { /// Create an input map with a single non-conditional layer. /// (Use [Default::default] to get an empty map.) pub fn new (layer: DslVal) -> Self { Self::default().layer(layer) } /// Add layer, return `Self`. pub fn layer (mut self, layer: DslVal) -> Self { self.add_layer(layer); self } /// Add conditional layer, return `Self`. pub fn layer_if (mut self, condition: DslVal, layer: DslVal) -> Self { self.add_layer_if(Some(condition), layer); self } /// Add layer, return `&mut Self`. pub fn add_layer (&mut self, layer: DslVal) -> &mut Self { self.add_layer_if(None, layer.into()); self } /// Add conditional layer, return `&mut Self`. pub fn add_layer_if (&mut self, condition: Option>, bindings: DslVal) -> &mut Self { self.0.push(InputLayer { condition, bindings }); self } /// Evaluate the active layers for a given state, /// returning the command to be executed, if any. pub fn handle (&self, state: &mut S, input: I) -> Perhaps where S: DslInto + DslInto, I: DslInto, O: Command { let layers = self.0.as_slice(); for InputLayer { condition, bindings } in layers.iter() { let mut matches = true; if let Some(condition) = condition { matches = state.dsl_into(condition, ||format!("input: no condition").into())?; } if matches && let Some(exp) = bindings.val().exp_head() && input.dsl_into(exp, ||format!("InputLayers: input.eval(binding) failed").into())? && let Some(command) = state.try_dsl_into(exp)? { return Ok(Some(command)) } } Ok(None) } }