diff options
| author | Teddy Wing | 2018-11-01 04:26:52 +0100 | 
|---|---|---|
| committer | Teddy Wing | 2018-11-01 04:41:57 +0100 | 
| commit | 6658bb238d87f2182f3c2a6119b86c5fdea9b36b (patch) | |
| tree | ab0a3f65104c97edb9fd6a0386aa8eed89ca6d3c /src/parser.rs | |
| parent | 13e90c090923a209e5e26fb3e609d5d12f737f53 (diff) | |
| download | dome-key-map-6658bb238d87f2182f3c2a6119b86c5fdea9b36b.tar.bz2 | |
parser: Propagate errors to the root parser
Thanks to Arnavion on Mozilla#rust for clueing me into this:
> The documentation starts with "A parser in this library can be
> described as a function which takes some input and if it is
> successful, returns a value together with the remaining input."
> Key phrase being "with the remaining input"
> So I assume it just matches as best as it can (which is nothing, in
> your case)
> Maybe you want to use some one-or-more combinator rather than many
> which is a zero-or-more combinator
> Then add your own check that "the remaining input" is empty
Replacing my `many()` parsers with `many1()` correctly propagates errors
up from sub-parers. Nice!
This will fail when we try to parse an empty string, but we can just add
a special case for that to parse successfully.
Diffstat (limited to 'src/parser.rs')
| -rw-r--r-- | src/parser.rs | 57 | 
1 files changed, 55 insertions, 2 deletions
| diff --git a/src/parser.rs b/src/parser.rs index 7afce9d..c7bccc0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -574,7 +574,7 @@ where      I: Stream<Item = char>,      I::Error: ParseError<I::Item, I::Range, I::Position>,  { -    many(map().skip(blank())) +    many1(map().skip(blank()))  }  fn map_collection<I>() -> impl Parser<Input = I, Output = MapCollection> @@ -631,7 +631,7 @@ where  {      (          blank(), -        many( +        many1(              choice!(                  try(map()).map(|map| Definition::Map(map)),                  try(mode()).map(|mode| Definition::Mode(mode)) @@ -1133,6 +1133,35 @@ cmd <down> echo test      }      #[test] +    fn map_collection_fails_without_terminating_newline() { +        let text = "map <play> works +map <down> fails"; +        let result = map_collection().easy_parse(State::new(text)).map(|t| t.0); + +        let mut expected = HashMap::new(); +        expected.insert( +            vec![HeadphoneButton::Play], +            MapAction { +                action: Action::String("works".to_owned()), +                kind: MapKind::Map, +            }, +        ); + +        assert_eq!(result, Err(easy::Errors { +            position: ::combine::stream::state::SourcePosition { +                line: 2, +                column: 17, +            }, +            errors: vec![ +                easy::Error::Unexpected("end of input".into()), +                easy::Error::Unexpected('f'.into()), +                easy::Error::Expected("whitespace".into()), +                easy::Error::Expected("tab".into()) +            ], +        })); +    } + +    #[test]      fn map_collection_parses_maps() {          let text = "  # Test comment @@ -1330,4 +1359,28 @@ cmd <play> /usr/bin/say hello          assert_eq!(result, Ok(expected));      } + +    #[test] +    fn map_group_with_invalid_input_fails() { +        let text = "not-a-kind <play> <Nop> +"; +        let result = map_group().easy_parse(State::new(text)).map(|t| t.0); + +        assert_eq!(result, Err(easy::Errors { +            position: ::combine::stream::state::SourcePosition { +                line: 1, +                column: 1, +            }, +            errors: vec![ +                easy::Error::Unexpected('n'.into()), +                easy::Error::Expected("map".into()), +                easy::Error::Expected("cmd".into()), +                easy::Error::Expected("mode".into()), +                easy::Error::Expected("lf newline".into()), +                easy::Error::Expected("whitespace".into()), +                easy::Error::Expected("tab".into()), +                easy::Error::Expected('#'.into()), +            ], +        })); +    }  } | 
