Closed svenstaro closed 6 years ago
There are actually only 4 errors here, it is just that cargo outputs them twice since I believe you are compiling in test mode (which is rather annoying).
error[E0271]: type mismatch resolving `<combine::range::TakeUntilRange<I> as combine::Parser>::Output == std::string::String`
--> src/lib.rs:30:26
|
30 | fn mt940_message<I>() -> impl Parser<Input = I, Output = String>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found struct `std::string::String`
|
= note: expected type `<I as combine::StreamOnce>::Range`
found type `std::string::String`
= note: the return type of a function must have a statically known size
take_until_range
outputs I::Range
, not a String
. If you want to return a String
and not rely on RangeStream
you can use https://docs.rs/combine/3.5.1/combine/parser/repeat/fn.take_until.html (not that it takes a parser, and not a range)
error[E0271]: type mismatch resolving `<I as combine::StreamOnce>::Range == &str`
--> src/lib.rs:35:5
|
35 | take_until_range("\r\n")
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found &str
|
= note: expected type `<I as combine::StreamOnce>::Range`
found type `&str`
"\r\n"
forces take_until_range
to have <I as combine::StreamOnce>::Range == &str
but you have not declared that in the signature, It should be I: RangeStream<Item = char, Range = &'a str>
error[E0277]: the trait bound `<I as combine::StreamOnce>::Range: combine::stream::Range` is not satisfied
--> src/lib.rs:30:26
|
30 | fn mt940_message<I>() -> impl Parser<Input = I, Output = String>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `combine::stream::Range` is not implemented for `<I as combine::StreamOnce>::Range`
|
= help: consider adding a `where <I as combine::StreamOnce>::Range: combine::stream::Range` bound
= note: required because of the requirements on the impl of `combine::Parser` for `combine::range::TakeUntilRange<I>`
= note: the return type of a function must have a statically known size
take_until_range
also requires I::Range: Range
https://docs.rs/combine/3.5.1/combine/stream/trait.Range.html
error[E0277]: the trait bound `I: combine::RangeStreamOnce` is not satisfied
--> src/lib.rs:35:5
|
35 | take_until_range("\r\n")
| ^^^^^^^^^^^^^^^^ the trait `combine::RangeStreamOnce` is not implemented for `I`
|
= help: consider adding a `where I: combine::RangeStreamOnce` bound
= note: required because of the requirements on the impl of `combine::RangeStream` for `I`
= note: required by `combine::range::take_until_range`
error: aborting due to 4 previous errors
You need to bound I: RangeStream
(not just I: Stream
)
That will fix the errors to make take_until_range
work but you could instead use https://docs.rs/combine/3.5.1/combine/parser/repeat/fn.take_until.html if you don't want to restrict your self to RangeStream
(implemented by&str
and &[T]
and wrappers around those) https://docs.rs/combine/3.5.1/combine/trait.RangeStreamOnce.html#foreign-impls
Following your suggestions in gitter, I came up with this:
fn mt940_tag<I>() -> impl Parser<Input = I, Output = String>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
token(':'),
char::digit(),
many::<String, _>(char::alpha_num()),
token(':'),
)
.map(|(_, x, y, _)| format!("{}{}", x, y))
}
fn mt940_record_start<I>() -> impl Parser<Input = I, Output = (String, String)>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(mt940_tag(), take_until(byte::bytes(&b"\r\n"[..])))
}
Which gives me
error[E0271]: type mismatch resolving `<I as combine::StreamOnce>::Item == u8`
--> src/lib.rs:28:31
|
28 | fn mt940_record_start<I>() -> impl Parser<Input = I, Output = (String, String)>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected char, found u8
|
= note: required because of the requirements on the impl of `combine::Parser` for `combine::byte::Bytes<I>`
= note: required because of the requirements on the impl of `combine::Parser` for `combine::combinator::TakeUntil<std::string::String, combine::byte::Bytes<I>>`
= note: the return type of a function must have a statically known size
error[E0271]: type mismatch resolving `<I as combine::StreamOnce>::Range == &[u8]`
--> src/lib.rs:28:31
|
28 | fn mt940_record_start<I>() -> impl Parser<Input = I, Output = (String, String)>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found &[u8]
|
= note: expected type `<I as combine::StreamOnce>::Range`
found type `&[u8]`
= note: required because of the requirements on the impl of `combine::Parser` for `combine::byte::Bytes<I>`
= note: required because of the requirements on the impl of `combine::Parser` for `combine::combinator::TakeUntil<std::string::String, combine::byte::Bytes<I>>`
= note: the return type of a function must have a statically known size
error[E0271]: type mismatch resolving `<I as combine::StreamOnce>::Item == u8`
--> src/lib.rs:33:19
|
33 | (mt940_tag(), take_until(byte::bytes(&b"\r\n"[..])))
| ^^^^^^^^^^ expected char, found u8
|
= note: required because of the requirements on the impl of `combine::Parser` for `combine::byte::Bytes<I>`
error[E0271]: type mismatch resolving `<I as combine::StreamOnce>::Range == &[u8]`
--> src/lib.rs:33:19
|
33 | (mt940_tag(), take_until(byte::bytes(&b"\r\n"[..])))
| ^^^^^^^^^^ expected associated type, found &[u8]
|
= note: expected type `<I as combine::StreamOnce>::Range`
found type `&[u8]`
= note: required because of the requirements on the impl of `combine::Parser` for `combine::byte::Bytes<I>`
error[E0271]: type mismatch resolving `<I as combine::StreamOnce>::Item == u8`
--> src/lib.rs:33:30
|
33 | (mt940_tag(), take_until(byte::bytes(&b"\r\n"[..])))
| ^^^^^^^^^^^ expected char, found u8
|
= note: required by `combine::byte::bytes`
error[E0271]: type mismatch resolving `<I as combine::StreamOnce>::Range == &[u8]`
--> src/lib.rs:33:30
|
33 | (mt940_tag(), take_until(byte::bytes(&b"\r\n"[..])))
| ^^^^^^^^^^^ expected associated type, found &[u8]
|
= note: expected type `<I as combine::StreamOnce>::Range`
found type `&[u8]`
= note: required by `combine::byte::bytes`
I tried giving it various types and mapping it but to no avail. Help appreciated. :)
These all boil down to the same error in the end and the only real way to solve it is to slowly read through the error to understand what rustc fails at.
The second from the bottom is probably the clearerest (by a slight margin).
error[E0271]: type mismatch resolving `<I as combine::StreamOnce>::Item == u8`
--> src/lib.rs:33:30
|
33 | (mt940_tag(), take_until(byte::bytes(&b"\r\n"[..])))
| ^^^^^^^^^^^ expected char, found u8
|
= note: required by `combine::byte::bytes`
Here it says that it expected a char
but found a u8
, more specifically it also says type mismatch resolving <I as combine::StreamOnce>::Item == u8
so apparently I::Item
were expected to be a char
but it was actually an u8
. So somewhere you have specified that I::Item == char
but elsewhere it is specified as u8
.
In this case you have specified I: Stream<Item = char>,
in the function signature so I assume that u8
is the error here so we must change that to use char
as well. Helpfully rustc points directly at the culprit, byte::bytes
is, as it name suggests, a parser that works with bytes (that is, u8
) so that parser needs to be replaced with a char
based one. In the char
module we can find https://docs.rs/combine/3.5.1/combine/parser/char/fn.string.html which is the equivalent to bytes
which will do the trick.
Any other of the errors could lead us to the same conclusion in a slight less clear way as they all boil down to trying to use u8
or &[u8]
where a string parser were expected. It is all about taking the time to dig into the error enough to understand and fix it, rustc --explain E0271
could be helpful as well if the error is confusing.
Oh, thanks! Well, reading the error is one thing but figuring out exactly what it means in relation to my code is another. I suggest adding an example for char::string to the take_until
docs.
So right now I'm here:
fn mt940_tag<I>() -> impl Parser<Input = I, Output = String>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
token(':'),
char::digit(),
many::<String, _>(char::alpha_num()),
token(':'),
)
.map(|(_, x, y, _)| format!("{}{}", x, y))
}
fn mt940_record_start<I>() -> impl Parser<Input = I, Output = (String, String)>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(mt940_tag(), take_until(char::string("\r\n")))
}
fn mt940_record<I>() -> impl Parser<Input = I, Output = String>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
mt940_record_start(),
many((not_followed_by(look_ahead(mt940_tag())), char::string("\r\n"))),
).map(|(_, _)| "test".to_string())
}
and it's telling me
error[E0277]: the trait bound `combine::error::Info<_, _>: std::convert::From<std::string::String>` is not satisfied
--> src/lib.rs:42:15
|
42 | many((not_followed_by(look_ahead(mt940_tag())), char::string("\r\n"))),
| ^^^^^^^^^^^^^^^ the trait `std::convert::From<std::string::String>` is not implemented for `combine::error::Info<_, _>`
|
= help: the following implementations were found:
<combine::error::Info<T, R> as std::convert::From<&'static str>>
<combine::error::Info<char, R> as std::convert::From<char>>
<combine::error::Info<u8, R> as std::convert::From<u8>>
= note: required because of the requirements on the impl of `std::convert::Into<combine::error::Info<_, _>>` for `std::string::String`
= note: required by `combine::not_followed_by`
The problem is that I can't just define that for String
for combine::error::Info
as that's not part of my own crate.
So I tried mapping that output to &str
:
fn mt940_record<I>() -> impl Parser<Input = I, Output = String>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
mt940_record_start(),
many((not_followed_by(look_ahead(mt940_tag()).map(|x| x.as_str())), char::string("\r\n"))),
).map(|(_, _)| "lol".to_string())
}
which then resulted in
error[E0283]: type annotations required: cannot resolve `_: std::iter::Extend<((), &'static str)>`
--> src/lib.rs:42:9
|
42 | many((not_followed_by(look_ahead(mt940_tag()).map(|x| x.as_str())), char::string("\r\n"))),
| ^^^^
|
= note: required by `combine::many`
error: aborting due to previous error
Ok so far so good. Now I needed annotations on many
as before. I want to end up with a String if I can help it, so:
fn mt940_record<I>() -> impl Parser<Input = I, Output = String>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
mt940_record_start(),
many::<String, _>((not_followed_by(look_ahead(mt940_tag()).map(|x| x.as_str())), char::string("\r\n"))),
).map(|(_, _)| "lol".to_string())
}
gets me
error[E0277]: the trait bound `std::string::String: std::iter::Extend<((), &'static str)>` is not satisfied
--> src/lib.rs:42:9
|
42 | many::<String, _>((not_followed_by(look_ahead(mt940_tag()).map(|x| x.as_str())), char::string("\r\n"))),
| ^^^^^^^^^^^^^^^^^ the trait `std::iter::Extend<((), &'static str)>` is not implemented for `std::string::String`
|
= help: the following implementations were found:
<std::string::String as std::iter::Extend<&'a char>>
<std::string::String as std::iter::Extend<&'a str>>
<std::string::String as std::iter::Extend<char>>
<std::string::String as std::iter::Extend<std::borrow::Cow<'a, str>>>
<std::string::String as std::iter::Extend<std::string::String>>
= note: required by `combine::many`
Now where does my static lifetime come from? I don't quite yet and frankly, I think I'm going down the wrong road on this. Please help. :)
Now where does my static lifetime come from? I don't quite yet and frankly, I think I'm going down the wrong road on this. Please help. :)
The static lifetime comes from what string
outputs (see impl Parser
at https://docs.rs/combine/3.5.1/combine/parser/char/struct.Str.html, not exactly easy to discover :/ )
the trait bound
std::string::String: std::iter::Extend<((), &'static str)>
is not satisfied
You are trying to extend with a tuple here as you are passing a tuple of parsers to many((a, b))
. You could either map over the tuple to only take the second field of it perhaps.
The problem is that I can't just define that for String for combine::error::Info as that's not part of my own crate. So I tried mapping that output to &str:
Just using as_str
will run you afoul of the borrow checker but you could give it your own &'static str
. Since not_followed_by
only use it to display an error message if the parser succeeds doing not_followed_by(look_ahead(mt940_tag()).map(|_| "mt940 tag"))
and errors will say "unexpected mt940 tag".
many::<String, _>
As an aside, do you really want a String
here? Or do you want a Vec<String>
, just thought I'd mention it (if it is just a String
then it will collect all of the lines into the same String
).
Thanks, that was the last bit I needed to actually make it compile. Types are magical. And I believe I want a String
here. See, a Record is a tag
and a message
and then a Statement (which is not actually a type in my code) is just a Vec<Record>
. My idea was to compose a bunch of parsers (that's the whole idea, right?).
Anyway, I believe the problem that I originally asked for help with in Gitter is not actually solved and that's how to parse the line format properly. For reference, I'm putting my whole bunch of code there.
The problem is that the parse_mt940_record
(and therefore also parse_mt940_statement
) test doesn't work. The problem must be with how I currently parse until \r\n
if the line doesn't start with a tag
. I'll try around a bit but I don't quite grok how to make the combinators behave so that I can express this need.
extern crate combine;
use combine::parser::char;
use combine::parser::repeat::take_until;
use combine::*;
#[derive(Debug, PartialEq)]
pub struct Record {
pub tag: String,
pub message: String,
}
fn mt940_tag<I>() -> impl Parser<Input = I, Output = String>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
token(':'),
char::digit(),
many::<String, _>(char::alpha_num()),
token(':'),
)
.map(|(_, x, y, _)| format!("{}{}", x, y))
}
fn mt940_record_start<I>() -> impl Parser<Input = I, Output = (String, String)>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(mt940_tag(), take_until(char::string("\r\n")))
}
fn mt940_record<I>() -> impl Parser<Input = I, Output = Record>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
(
mt940_record_start(),
many::<String, _>(
(
not_followed_by(look_ahead(mt940_tag()).map(|_| "mt940_tag")),
char::string("\r\n"),
)
.map(|(_, x)| x),
),
)
.map(|(start, rest)| Record {
tag: start.0,
message: format!("{start}{rest}", start = start.1, rest = rest),
})
}
/// A Statement is just a list of Records.
fn mt940_statement<I>() -> impl Parser<Input = I, Output = Vec<Record>>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
many1::<Vec<_>, _>(mt940_record())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_mt940_tag() {
let expected = Ok(("20".to_string(), ""));
let result = mt940_tag().easy_parse(":20:");
assert_eq!(expected, result);
}
#[test]
fn parse_mt940_record_start() {
let expected = Ok((("20".to_string(), "3996-11-11111111".to_string()), "\r\n"));
let result = mt940_record_start().easy_parse(":20:3996-11-11111111\r\n");
assert_eq!(expected, result);
}
#[test]
fn parse_mt940_record() {
let expected = Ok((
Record {
tag: "20".to_string(),
message: "3996-11-11111111\r\nTESTTEST\r\nMORETEST\r\n".to_string(),
},
"",
));
let result = mt940_record().easy_parse(":20:3996-11-11111111\r\nTESTTEST\r\nMORETEST\r\n");
assert_eq!(expected, result);
}
#[test]
fn parse_mt940_statement() {
let test_data = "\
:20:3996-1234567890\r\n\
:25:DABADKKK/1234567890\r\n\
:28C:00014/001\r\n\
:60F:C091019DKK3859701,48\r\n\
:86:For your inform. IBAN no.: DK5030001234567890\r\n\
:86:DABADKKK \r\n\
:86:1234567890\r\n\
:86:DANSKE BANK HOLMENS KANAL 2-12\r\n\
:61:0910201020DK5312,50NMSCDBT.teste kunden\r\n\
:86:F.M.T.\r\n\
V/TESTE KUNDEN\r\n\
HOLMENS KANAL 2-12\r\n\
1192 KOBENHAVN H\r\n\
:61:0910201020DK3009,51NMSCDBT.Pet Van Park\r\n\
:86:DBTS 1111272333/Bnf. PET VAN PARK AMSTERDAM/Bnf.acc. NL47ABNAXXXX\r\n\
558756/Our fee DKK 40,00/Foreign fee DKK 200,00\r\n\
:62F:C091020DKK3851379,47\r\n\
:64:C091020DKK3851379,47\r\n\
\r\n
";
let expected = Ok((
vec![
Record {
tag: "20".to_string(),
message: "3996-1234567890".to_string(),
},
Record {
tag: "25".to_string(),
message: "DABADKKK/1234567890".to_string(),
},
Record {
tag: "28C".to_string(),
message: "00014/001".to_string(),
},
Record {
tag: "60F".to_string(),
message: "C091019DKK3859701,48".to_string(),
},
Record {
tag: "86".to_string(),
message: ")
For your inform. IBAN no.: DK5030001234567890\n\
DABADKKK\n\
1234567890\n\
DANSKE BANK HOLMENS KANAL 2-12\n\
".to_string(),
},
Record {
tag: "61".to_string(),
message: "0910201020DK5312,50NMSCDBT.teste kunden".to_string(),
},
Record {
tag: "86".to_string(),
message: "F.M.T.\n\
V/TESTE KUNDEN\n\
HOLMENS KANAL 2-12\n\
1192 KOBENHAVN H\n\
".to_string(),
},
Record {
tag: "61".to_string(),
message: "0910201020DK3009,51NMSCDBT.Pet Van Park".to_string(),
},
Record {
tag: "86".to_string(),
message: "DBTS 1111272333/Bnf. PET VAN PARK AMSTERDAM/Bnf.acc. NL47ABNAXXXX\n\
558756/Our fee DKK 40,00/Foreign fee DKK 200,00"
.to_string(),
},
Record {
tag: "62F".to_string(),
message: "C091020DKK3851379,47".to_string(),
},
Record {
tag: "64".to_string(),
message: "C091020DKK3851379,47".to_string(),
},
],
"",
));
let result = mt940_statement().easy_parse(test_data);
assert_eq!(expected, result);
}
}
(
not_followed_by(look_ahead(mt940_tag()).map(|_| "mt940_tag")),
char::string("\r\n"),
)
I think you are just missing a take_until
wrapper around char::string("\r\n")
:)
I believe we worked this out on gitter, so closing (feel free to re-open if necessary).
So I have a parser that looks like this:
But that gives me a lot of errors:
I really only want a parser that recognizes something until "\r\n".