I'm struggling to see the point in this. Your roll example using Winnow. Not really that much (un)wrapping logic:
use winnow::{Result, ascii::digit1, combinator::separated_pair, prelude::*};
#[derive(Debug, PartialEq)]
struct Roll {
count: u32,
sides: u32,
}
fn number(input: &mut &str) -> Result<u32> {
digit1.try_map(str::parse).parse_next(input)
}
fn roll(input: &mut &str) -> Result<Roll> {
let (count, sides) = separated_pair(number, 'd', number).parse_next(input)?;
Ok(Roll { count, sides })
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_roll() {
let input = &mut "3d6";
assert_eq!(roll(input), Ok(Roll { count: 3, sides: 6 }));
}
}
In this case yes, but I would argue this is because the roll grammar is a relatively simple grammar, you have no nesting of different combinators etc. (I have used it in the README because it is simple to understand). The winnow code also using seperated_pair to ignore the "d" value, which is also adding some logic to retain only the count and sides result.
One grammar I have been using to make a comparison is a function signature. I tried comparing with nom and chumsky: https://blog.arnedebo.com/posts/a-grammar-first-approach-to-...
These libraries have found ways to replace "unwrapping" code with combinators like separated_by or .ignore_then etc. but this still leaves a lot of code for result handling, in my opinon making the code harder to read and reason about, which is what I attempt to improve.
These are of course very subjective things, so another person might prefer the ways the other libraries do this.