Hi everyone.
I've two nom parsers for redis RESP SET
command input
fn expiry_to_timestamp(expiry: &ExpiryOption) -> anyhow::Result<u64> {
// u64 always since u32 secs fits into u64
// get the current system time
let now = SystemTime::now();
// how many seconds have elapsed since beginning of time
let duration_since_epoch = now
.duration_since(UNIX_EPOCH)
.context("This should always succeed")?;
// we don't want to lose precision between seconds & milliseconds
match expiry {
ExpiryOption::Seconds(seconds) => Ok(*seconds as u64 + duration_since_epoch.as_secs()),
ExpiryOption::Milliseconds(milliseconds) => {
Ok(milliseconds + duration_since_epoch.as_millis() as u64) // nominally millis are u128
}
}
}
map_res(
tuple((tag_no_case("$2\r\nEX\r\n"), parse_resp_string)),
|(_, seconds_str)| {
seconds_str
.parse::<u32>()
.map_err(|_e: ParseIntError| {
nom::error::Error::new(
seconds_str.clone(),
nom::error::ErrorKind::Digit,
)
})
.and_then(|seconds| {
expiry_to_timestamp(&ExpiryOption::Seconds(seconds))
.map_err(|_| {
nom::error::Error::new(
seconds_str.clone(),
nom::error::ErrorKind::Verify,
)
})
.map(|timestamp| SetCommandExpireOption::EX(timestamp as u32))
})
},
),
and the problem is, even if I give it SET foo bar ex blah
, i.e. telling it to expire foo in blah
seconds, it still returns "OK".
Now, if I remove all those map_err
and just do an expect
then it correctly fails but it also panics, severing my client connection.
There are some suggestions from random google searches, that perhaps the issue here is that map_res
is not failing the parser when the inner Result
is an Err
. Instead, it's creating a nom error but not propagating it correctly. Prob because map_res
expects the error to be returned as part of the IResult
type, not as a nom::error::Error
But now I'm getting throughly lost on all this: how do I make it fail as it is supposed to?