use crate::*; use std::{sync::Arc, collections::BTreeMap, path::{Path, PathBuf}, fs::{exists, read_to_string}}; /// Map of each event (e.g. key combination) to /// all command expressions bound to it by /// all loaded input layers. type EventMapImpl = BTreeMap>>; /// 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 cond, /// so that only certain layers are active at a given time depending on state. /// /// When a key is pressed, the bindings for it are checked in sequence. /// When the first non-conditional or true conditional binding is executed, /// that .event()binding's value is returned. #[derive(Debug)] pub struct EventMap(pub EventMapImpl); /// An input binding. #[derive(Debug, Clone)] pub struct Binding { pub command: C, pub condition: Option, pub description: Option>, pub source: Option>, } /// Input bindings are only returned if this evaluates to true #[derive(Clone)] pub struct Condition(Arcbool + Send + Sync>>); impl_debug!(Condition |self, w| { write!(w, "*") }); /// Default is always empty map regardless if `E` and `C` implement [Default]. impl Default for EventMap { fn default () -> Self { Self(Default::default()) } } impl EventMap { /// Create a new event map pub fn new () -> Self { Default::default() } /// Add a binding to an owned event map. pub fn def (mut self, event: E, binding: Binding) -> Self { self.add(event, binding); self } /// Add a binding to an event map. pub fn add (&mut self, event: E, binding: Binding) -> &mut Self { if !self.0.contains_key(&event) { self.0.insert(event.clone(), Default::default()); } self.0.get_mut(&event).unwrap().push(binding); self } /// Return the binding(s) that correspond to an event. pub fn query (&self, event: &E) -> Option<&[Binding]> { self.0.get(event).map(|x|x.as_slice()) } /// Return the first binding that corresponds to an event, considering conditions. pub fn dispatch (&self, event: &E) -> Option<&Binding> { self.query(event) .map(|bb|bb.iter().filter(|b|b.condition.as_ref().map(|c|(c.0)()).unwrap_or(true)).next()) .flatten() } /// Create event map from path to text file. pub fn load_from_path <'s> ( &'s mut self, path: impl AsRef ) -> Usually<&mut Self> where Self: DslInto + DslInto { if exists(path.as_ref())? { let source = read_to_string(&path)?; let path: Arc = Arc::new(path.as_ref().into()); self.load_from_source(&source, &Some(&path)) } else { return Err(format!("(e5) not found: {:?}", path.as_ref()).into()) } } /// Create event map from DSL tokenizer. pub fn load_from_source ( &mut self, dsl: impl Dsl, path: &Option<&Arc> ) -> Usually<&mut Self> where Self: DslInto + DslInto { dsl.each(|dsl|self.load_from_source_one(&dsl, path).map(move|_|()))?; Ok(self) } /// Load one event binding into the event map. pub fn load_from_source_one <'s> ( &'s mut self, dsl: impl Dsl, path: &Option<&Arc> ) -> Usually<&mut Self> where Self: DslInto + DslInto { if let Some(exp) = dsl.head()?.exp()? && let Some(sym) = exp.head()?.sym()? && let Some(tail) = exp.tail()? { let event = self.dsl_into_or_else(&sym, ||panic!())?; let command = self.dsl_into_or_else(&tail, ||panic!())?; Ok(self.add(event, Binding { command, condition: None, description: None, source: path.cloned() })) } else { Err(format!("unexpected: {:?}", dsl.head()?).into()) } } //})Ok(if let Some(sym) = dsl.head()?.exp()?.head()?.sym()? { //if let Some(tail) = dsl.head()?.exp()?.tail()? { //let event: E = sym.into(); //let binding: Binding = Binding { command: tail.into(), condition: None, description: None, source: None }; //if let Some(bindings) = map.0.get_mut(&event) { //bindings.push(binding); //} else { //map.0.insert(event, vec![binding]); //} //} else { //panic!("empty binding: {}", dsl.head()?.exp()?.unwrap_or_default()) //} //} else if let Some(ref text) = dsl.text()? { //map.0.extend(Self::from_path(PathBuf::from(text))?.0); //} else { //return Err(format!("unexpected: {dsl:?}").into()) //})); //Ok(map) //} } impl Binding { pub fn from_dsl (dsl: impl Dsl) -> Usually { let mut command: Option = None; let mut condition: Option = None; let mut description: Option> = None; let mut source: Option> = None; if let Some(command) = command { Ok(Self { command, condition, description, source }) } else { Err(format!("no command in {dsl:?}").into()) } } } fn unquote (x: &str) -> &str { let mut chars = x.chars(); chars.next(); //chars.next_back(); chars.as_str() }