Closed YjyJeff closed 8 months ago
I've had similar thoughts before, but a repeatable structure has never really emerged. For example, for Advent of Code, I've written a number of things where I use the wrong value in the error:
Ok(match c {
'a' => Thing::A,
'b' => Thing::B,
other => return InvalidSnafu { other }.fail(),
})
Some questions to tease out details...
In similar cases, I use SNAFU as part of a bigger picture. I'll often define an enum so that each variant is exactly zero or one tuple, then sub structs with the details and conversion methods:
use snafu::prelude::*;
#[derive(Debug)]
enum Input {
A(A),
B(B),
}
#[derive(Debug)]
struct A(String);
#[derive(Debug)]
struct B(i64);
impl From<A> for Input {
fn from(other: A) -> Self {
Self::A(other)
}
}
impl TryFrom<Input> for A {
type Error = InvalidError;
fn try_from(original: Input) -> Result<Self, Self::Error> {
match original {
Input::A(v) => Ok(v),
original => InvalidSnafu { original }.fail(),
}
}
}
#[derive(Debug, Snafu)]
#[snafu(display("The enum was not the requested type"))]
struct InvalidError {
original: Input,
}
I then often create an enum to generate that for me:
use snafu::prelude::*;
macro_rules! awesome {
(
enum $e_name:ident {
$(
$v_name:ident($v_field:ty)
,)+
}
) => (
#[derive(Debug)]
enum $e_name {
$($v_name($v_name),)*
}
$(
#[derive(Debug)]
struct $v_name($v_field);
impl From<$v_name> for $e_name {
fn from(other: $v_name) -> Self {
Self::$v_name(other)
}
}
impl TryFrom<$e_name> for $v_name {
type Error = InvalidError;
fn try_from(original: $e_name) -> Result<Self, Self::Error> {
match original {
Input::$v_name(v) => Ok(v),
original => InvalidSnafu { original }.fail(),
}
}
}
)*
);
}
awesome! {
enum Input {
A(String),
B(i64),
}
}
#[derive(Debug, Snafu)]
#[snafu(display("The enum was not the requested type"))]
struct InvalidError {
original: Input,
}
I'm certain there are crates out there that do the same as that macro (likely even better!), but I tend to write the one-off each time I need it.
In some of the cases, we need to ensure the variable is a specific variant and return an error if the pattern does not match. We can achieve it with
let-else
now. For example:In the snafu, we encourage the user to use ensure macros to check the condition. Could we provide a new macro
ensure_variant
to check the pattern matching?