aboutsummaryrefslogtreecommitdiffstats
path: root/src/parser.rs
diff options
context:
space:
mode:
authorTeddy Wing2018-11-01 04:26:52 +0100
committerTeddy Wing2018-11-01 04:41:57 +0100
commit6658bb238d87f2182f3c2a6119b86c5fdea9b36b (patch)
treeab0a3f65104c97edb9fd6a0386aa8eed89ca6d3c /src/parser.rs
parent13e90c090923a209e5e26fb3e609d5d12f737f53 (diff)
downloaddome-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.rs57
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()),
+ ],
+ }));
+ }
}