diff options
| author | Teddy Wing | 2018-08-22 01:42:39 +0200 |
|---|---|---|
| committer | Teddy Wing | 2018-08-22 01:42:39 +0200 |
| commit | 5f670cf87ac217930babb0b8ef0f953fd2ee3447 (patch) | |
| tree | 29895af69ae4058cad972acc197777bceb93aed3 /src/lib.rs | |
| parent | 62f649a368bee6c7dbff88d7d599637bb9c5d184 (diff) | |
| download | dome-key-map-5f670cf87ac217930babb0b8ef0f953fd2ee3447.tar.bz2 | |
Move all code in `lib.rs` to `parser.rs`
Put our parsing code in its own module. We'll be creating a new `cocoa`
module and doing this allows us to keep `lib` free as a module
container.
Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 514 |
1 files changed, 1 insertions, 513 deletions
@@ -1,516 +1,4 @@ #[macro_use] extern crate combine; -use std::collections::HashMap; - -use combine::*; -use combine::parser::choice::or; -use combine::parser::char::{ - newline, - space, - string, - string_cmp, - tab, -}; -use combine::parser::repeat::take_until; - -#[derive(Debug, Hash, Eq, PartialEq)] -pub enum HeadphoneButton { - Play, - Up, - Down, -} -type Trigger = Vec<HeadphoneButton>; -type Action = String; - -#[derive(Debug, PartialEq)] -pub enum MapKind { - Map, - Command, -} - -#[derive(Debug, PartialEq)] -pub struct MapAction { - pub action: Action, - pub kind: MapKind, -} - -#[derive(Debug, PartialEq)] -struct Map { - trigger: Trigger, - action: Action, - kind: MapKind, -} - -type MapCollection = HashMap<Trigger, MapAction>; - -#[derive(Debug, PartialEq)] -struct Mode { - trigger: Trigger, - maps: MapCollection, -} - -#[derive(Debug, PartialEq)] -pub struct MapGroup { - maps: MapCollection, - modes: HashMap<Trigger, MapCollection>, -} - -#[derive(Debug, PartialEq)] -enum Definition { - Map(Map), - Mode(Mode), -} - - -fn map_kind<I>() -> impl Parser<Input = I, Output = MapKind> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - or( - string("map").map(|_| MapKind::Map), - string("cmd").map(|_| MapKind::Command), - ) -} - -fn headphone_button<I>() -> impl Parser<Input = I, Output = HeadphoneButton> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - 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<I>() -> impl Parser<Input = I, Output = Trigger> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - many1(headphone_button()) -} - -fn action<I>() -> impl Parser<Input = I, Output = Action> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - take_until(newline()) -} - -fn whitespace_separator<I>() -> impl Parser<Input = I> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - skip_many1(space().or(tab())) -} - -fn map<I>() -> impl Parser<Input = I, Output = Map> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - ( - map_kind(), - whitespace_separator(), - trigger(), - whitespace_separator(), - action() - ).map(|(kind, _, trigger, _, action)| - Map { - trigger: trigger, - action: action, - kind: kind, - } - ) -} - -fn map_collection<I>() -> impl Parser<Input = I, Output = MapCollection> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - ( - blank(), - many::<Vec<Map>, _>(map().skip(blank())), - ).map(|(_, collection)| { - let mut maps = HashMap::new(); - - for map in collection { - maps.insert( - map.trigger, - MapAction { - action: map.action, - kind: map.kind, - } - ); - } - - maps - }) -} - -fn mode<I>() -> impl Parser<Input = I, Output = Mode> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - ( - string("mode"), - whitespace_separator(), - trigger(), - whitespace_separator(), - token('{'), - map_collection(), - token('}'), // Verify that this is parsed on its own line, not inside a map - ).map(|(_, _, trigger, _, _, collection, _)| - Mode { - trigger: trigger, - maps: collection, - } - ) -} - -fn definitions<I>() -> impl Parser<Input = I, Output = Vec<Definition>> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - ( - blank(), - many( - choice!( - try(map()).map(|map| Definition::Map(map)), - try(mode()).map(|mode| Definition::Mode(mode)) - ).skip(blank()) - ) - ).map(|(_, definitions)| definitions) -} - -fn map_group<I>() -> impl Parser<Input = I, Output = MapGroup> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - definitions() - .map(|definitions| { - let mut maps = HashMap::new(); - let mut modes = HashMap::new(); - - for definition in definitions { - match definition { - Definition::Map(map) => { - maps.insert( - map.trigger, - MapAction { - action: map.action, - kind: map.kind, - } - ); - }, - Definition::Mode(mode) => { - modes.insert( - mode.trigger, - mode.maps, - ); - }, - } - } - - MapGroup { - maps: maps, - modes: modes, - } - }) -} - -fn comment<I>() -> impl Parser<Input = I> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - ( - token('#'), - skip_many(satisfy(|c| c != '\n')), - ) -} - - -fn blank<I>() -> impl Parser<Input = I> -where - I: Stream<Item = char>, - I::Error: ParseError<I::Item, I::Range, I::Position>, -{ - let comment = comment().map(|_| ()); - let whitespace = whitespace_separator().map(|_| ()); - skip_many(skip_many1(newline()).or(whitespace).or(comment)) -} - - -#[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 = "<play>"; - let result = headphone_button().parse(text).map(|t| t.0); - - assert_eq!(result, Ok(HeadphoneButton::Play)); - } - - #[test] - fn headphone_button_ignores_case() { - let text = "<Play>"; - 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 = "<up><down><play>"; - 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)); - } - - #[test] - fn map_parses_map_line() { - let text = "map <play><down> test -"; - let expected = Map { - trigger: vec![HeadphoneButton::Play, HeadphoneButton::Down], - action: "test".to_owned(), - kind: MapKind::Map, - }; - let result = map().parse(text).map(|t| t.0); - - assert_eq!(result, Ok(expected)); - } - - #[test] - fn map_collection_parses_maps() { - let text = " -# Test comment - # continued - -map <up><down> test -map <play> salt and pepper - -# Another comment -cmd <down> /usr/bin/say 'hello' -"; - let result = map_collection().easy_parse(text).map(|t| t.0); - - let mut expected = HashMap::new(); - expected.insert( - vec![HeadphoneButton::Up, HeadphoneButton::Down], - MapAction { - action: "test".to_owned(), - kind: MapKind::Map, - }, - ); - expected.insert( - vec![HeadphoneButton::Play], - MapAction { - action: "salt and pepper".to_owned(), - kind: MapKind::Map, - }, - ); - expected.insert( - vec![HeadphoneButton::Down], - MapAction { - action: "/usr/bin/say 'hello'".to_owned(), - kind: MapKind::Command, - }, - ); - - assert_eq!(result, Ok(expected)); - } - - #[test] - fn mode_parses_a_mode() { - let text = "mode <down><up> { - cmd <up><play> echo hello - map <down> insert {} - }"; - let result = mode().parse(text).map(|t| t.0); - - let mut expected = Mode { - trigger: vec![HeadphoneButton::Down, HeadphoneButton::Up], - maps: HashMap::new(), - }; - - expected.maps.insert( - vec![HeadphoneButton::Up, HeadphoneButton::Play], - MapAction { - action: "echo hello".to_owned(), - kind: MapKind::Command, - }, - ); - expected.maps.insert( - vec![HeadphoneButton::Down], - MapAction { - action: "insert {}".to_owned(), - kind: MapKind::Map, - }, - ); - - assert_eq!(result, Ok(expected)); - } - - #[test] - fn definitions_parses_modes_and_maps() { - let text = " - -mode <up> { - cmd <down> j -} -map <play> m -mode <down><up> { - cmd <down> j -} - -map <down> k -"; - let result = definitions().easy_parse(text).map(|t| t.0); - - let mut mode_up_maps = HashMap::new(); - mode_up_maps.insert( - vec![HeadphoneButton::Down], - MapAction { - action: "j".to_owned(), - kind: MapKind::Command, - } - ); - - let mut mode_down_up_maps = HashMap::new(); - mode_down_up_maps.insert( - vec![HeadphoneButton::Down], - MapAction { - action: "j".to_owned(), - kind: MapKind::Command, - } - ); - - let expected = vec![ - Definition::Mode(Mode { - trigger: vec![HeadphoneButton::Up], - maps: mode_up_maps, - }), - Definition::Map(Map { - trigger: vec![HeadphoneButton::Play], - action: "m".to_owned(), - kind: MapKind::Map, - }), - Definition::Mode(Mode { - trigger: vec![HeadphoneButton::Down, HeadphoneButton::Up], - maps: mode_down_up_maps, - }), - Definition::Map(Map { - trigger: vec![HeadphoneButton::Down], - action: "k".to_owned(), - kind: MapKind::Map, - }), - ]; - - assert_eq!(result, Ok(expected)); - } - - #[test] - fn map_group_parses_a_whole_map_file_string() { - let text = "map <play> some text - -# The following does nothing -cmd <down> /bin/echo nothing - -mode <down><up> { - map <play> p -} - -cmd <play> /usr/bin/say hello -"; - let result = map_group().easy_parse(text).map(|t| t.0); - - let mut maps: MapCollection = HashMap::new(); - let mut modes: HashMap<Trigger, MapCollection> = HashMap::new(); - let mut mode_maps: MapCollection = HashMap::new(); - - maps.insert( - vec![HeadphoneButton::Down], - MapAction { - action: "/bin/echo nothing".to_owned(), - kind: MapKind::Command, - }, - ); - maps.insert( - vec![HeadphoneButton::Play], - MapAction { - action: "/usr/bin/say hello".to_owned(), - kind: MapKind::Command, - }, - ); - - mode_maps.insert( - vec![HeadphoneButton::Play], - MapAction { - action: "p".to_owned(), - kind: MapKind::Map, - }, - ); - modes.insert( - vec![HeadphoneButton::Down, HeadphoneButton::Up], - mode_maps, - ); - - let expected = MapGroup { - maps: maps, - modes: modes, - }; - - assert_eq!(result, Ok(expected)); - } -} +mod parser; |
