Given a sequence of numbers, b"12345678..."
, is there an easy way to have nom
parse just the first n
digits of the input into a type, such as u16
, then the subsequent m
digits into another type, and so on. Presently, nom
consumes the entire contiguous sequence of digits into a single value.
The current application is for a sequence of digits representing datetime, as in, YYYYMMDDHHmmSS
, from the input, (D:20230416140523Z00'00)
My attempts combining take(n)
have not been successful. For instance...
use nom::bytes::complete::take;
use nom::character::complete::u16;
use nom::combinator::{flat_map};
type IResult<'a, T> = nom::IResult<&'a [u8], T, ()>;
fn parse_fixed_length_prefix<'a>(input: &[u8], n: usize) -> IResult<u16> {
flat_map(take(n), u16::<_, ()>)(input)
}
#[test]
fn test() {
let output = parse_fixed_length_prefix(b"123456789", 4);
assert_eq!(output, Ok((b"56789".as_ref(), 1234u16)));
// expected: Ok(([56789], 1234))
}
The above approach results in the following compile error, however I suspect there is a better approach that would obviate this...
Compiling playground v0.0.1 (/playground)
error[E0277]: expected a `FnMut<(_,)>` closure, found `Result<(_, u16), nom::Err<()>>`
--> src/main.rs:7:5
|
7 | flat_map(take(n), u16::<_, ()>)(input)
| ^^^^^^^^ expected an `FnMut<(_,)>` closure, found `Result<(_, u16), nom::Err<()>>`
|
= help: the trait `FnMut<(_,)>` is not implemented for `Result<(_, u16), nom::Err<()>>`
= help: the following other types implement trait `Parser<I, O, E>`:
<And<F, G> as Parser<I, (O1, O2), E>>
<AndThen<F, G, O1> as Parser<I, O2, E>>
<Box<(dyn Parser<I, O, E> + 'a)> as Parser<I, O, E>>
<Or<F, G> as Parser<I, O, E>>
<nom::FlatMap<F, G, O1> as Parser<I, O2, E>>
<nom::Into<F, O1, O2, E1, E2> as Parser<I, O2, E2>>
<nom::Map<F, G, O1> as Parser<I, O2, E>>
= note: required for `Result<(_, u16), nom::Err<()>>` to implement `Parser<_, _, _>`
note: required by a bound in `nom::combinator::flat_map`
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/nom-7.1.3/src/combinator/mod.rs:213:6
|
213 | H: Parser<I, O2, E>,
| ^^^^^^^^^^^^^^^^ required by this bound in `flat_map`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
Don't use nom:combination::flat_map
. Use nom:combination::map_res
to transform the output of take(n)
to the desired type, u16
, then use map
of the Result
type to transform the result to the value, Ok<u16>
, that map_res
can integrate into its output, in this case IResult<&[u8], u16, ()>
.
fn parse_fixed_length_prefix<'a>(input: &[u8], n: usize) -> IResult<u16> {
map_res(take(n), |v| u16::<_,()>(v).map(|(_,a)|a))(input)
}
One of the things I like about Rust is that if it compiles it works, even more so than Haskell.
Update solution per the recommendations of @ChayimFriedman
fn parse_fixed_length_prefix<'a>(input: &[u8], n: usize) -> IResult<u16> {
map_parser(take(n), u16)(input)
}