#[macro_use] extern crate combine; use std::collections::HashMap; use combine::*; use combine::parser::choice::or; use combine::parser::char::{newline, string, string_cmp}; use combine::parser::repeat::take_until; #[derive(Debug, PartialEq)] pub enum HeadphoneButton { Play, Up, Down, } type Trigger = Vec; type Action = String; #[derive(Debug, PartialEq)] pub enum MapKind { Map, Command, } pub struct Map { pub action: Action, pub kind: MapKind, } type MapCollection = HashMap; pub struct DKMapGroup { maps: MapCollection, modes: HashMap, } fn map_kind() -> impl Parser where I: Stream, I::Error: ParseError, { or( string("map").map(|_| MapKind::Map), string("cmd").map(|_| MapKind::Command), ) } fn headphone_button() -> impl Parser where I: Stream, I::Error: ParseError, { between( token('<'), token('>'), choice!( string_cmp("play", |l, r| l.eq_ignore_ascii_case(&r)) .map(|_| HeadphoneButton::Play), string_cmp("up", |l, r| l.eq_ignore_ascii_case(&r)) .map(|_| HeadphoneButton::Up), string_cmp("down", |l, r| l.eq_ignore_ascii_case(&r)) .map(|_| HeadphoneButton::Down) ), ) } fn trigger() -> impl Parser where I: Stream, I::Error: ParseError, { many1(headphone_button()) } fn action() -> impl Parser where I: Stream, I::Error: ParseError, { take_until(newline()) } #[cfg(test)] mod tests { use super::*; #[test] fn map_kind_parses_kind_map() { let text = "map"; let result = map_kind().parse(text).map(|t| t.0); assert_eq!(result, Ok(MapKind::Map)); } #[test] fn map_kind_parses_kind_command() { let text = "cmd"; let result = map_kind().parse(text).map(|t| t.0); assert_eq!(result, Ok(MapKind::Command)); } #[test] fn headphone_button_parses_play() { let text = ""; let result = headphone_button().parse(text).map(|t| t.0); assert_eq!(result, Ok(HeadphoneButton::Play)); } #[test] fn headphone_button_ignores_case() { let text = ""; let result = headphone_button().parse(text).map(|t| t.0); assert_eq!(result, Ok(HeadphoneButton::Play)); } #[test] fn trigger_parses_headphone_button_sequence() { let text = ""; let result = trigger().parse(text).map(|t| t.0); assert_eq!(result, Ok(vec![ HeadphoneButton::Up, HeadphoneButton::Down, HeadphoneButton::Play, ])); } #[test] fn action_parses_string_to_end_of_line() { let text = "/usr/bin/say 'hello' "; let expected: Action = "/usr/bin/say 'hello'".to_owned(); let result = action().parse(text).map(|t| t.0); assert_eq!(result, Ok(expected)); } }