greyblake / nutype

Rust newtype with guarantees πŸ‡ΊπŸ‡¦ πŸ¦€
MIT License
1.4k stars 23 forks source link

Support custom errors #161

Closed greyblake closed 1 month ago

greyblake commented 2 months ago

Context

Some orgs and people are not quite happy with auto-generated error types, e.g. it's not possible to customize them, inject extra details, etc.

Requirement

Provide a way for users to:

Technically it's possible, but the syntax for this needs to be decided.

Consideration

Having this may clash with "unified error": https://github.com/greyblake/nutype/issues/75, so requires careful thinking.

boyswan commented 2 months ago

I would love to see this - is one of the main reasons I'm unable to use nutype

greyblake commented 1 month ago

@AlisCode @gobanos @boyswan What do you guys think of the following syntax?

#[nutype(
    sanitize(trim),
    validate(with = validate_name, error = NameError),
    derive(Debug, AsRef, PartialEq, Deref),
)]
struct Name(String);

where

fn validate_name(name: &str) -> Result<(), NameError> {
    if name.len() < 3 {
        Err(NameError::TooShort)
    } else if name.len() > 20 {
        Err(NameError::TooLong)
    } else {
        Ok(())
    }
}

// This actually probably will need to implement fully std::error::Error
#[derive(Debug)]
enum NameError {
    TooShort,
    TooLong,
}

Do you have any alternative proposal to consider?

boyswan commented 1 month ago

Looks great!

AlisCode commented 1 month ago

Looks cool!

Although I have to say this breaks a lot with the current nutype philosophy.

Right now, nutype supports e.g. the following attribute :

validate(not_empty, len_char_max = 20), which produces an error enum with 2 variants (NotEmptyViolated, LenCharMaxViolated)

I think it's a good feat of nutype that you're able to provide a multi-step validation, and this proposal attemps to do exactly the reverse which is that you would provide one custom validation function which would contain all your logic in one place. It would probably mean having a lot of code dedicated to the special case.

What if instead, nutype still produced an enum which contains one variant for each step of the validation ?

For example :

struct TooLongError;
fn max_len(input: &str) -> Result<(), TooLongError> {
    if name.len() > 20 {
        Err(TooLongError)
    } else {
        Ok(())
    }
}

struct TooShortError;
fn min_len(input: &str) -> Result<(), TooShortError> {
    if name.len() < 3 {
        Err(TooShortError)
    } else {
        Ok(())
    }
}

#[nutype(
    sanitize(trim),
    validate(
        with(max_len, variant = TooLong),
        with(min_len, variant = TooShort),
    )
    derive(Debug, AsRef, PartialEq, Deref),
)]
struct Name(String);

Produced code :

enum NameError { 
    TooLong(TooLongError),
    TooShort(TooShortError),
    // And here you can support other potential validation steps ...
}

Also right now nutype supports passing in arguments to validators such as for len_char_max = 20. What if the customized validation function needs to be reused between different newtypes, and you wanted to pass in an argument just like for len_char_max ? How do you see that working ?

greyblake commented 1 month ago

@AlisCode Thank you for your valuable input! I had a similar thing to what you suggest in mind, though I see it as an orthogonal to this story (support of custom errors VS custom error variants). Ideally I'd like to have both in some or other form implement, because they serve different needs.

DJDuque commented 1 month ago

What if instead, nutype still produced an enum which contains one variant for each step of the validation ?

This would "pollute" (even more than it currently does) the public API of libraries that want to use nutype. Maybe a different approach, more centered towards fixing #75, fits better?

DJDuque commented 1 month ago

Maybe good inspiration on how to handle "let users customize errors" can be taken from parser combinator libraries? Feels like nutype validation errors can take inspiration on how they handle parsing errors (?) I don't know, just throwing ideas out there.

Maybe there is something to copy from the work that @epage has done in winnow? Again, I don't know, just throwing random ideas around.

greyblake commented 1 month ago

@DJDuque Could you explain how winnow error idea would fit here?

greyblake commented 1 month ago

@boyswan @AlisCode @DJDuque I just released 0.5.0-beta.2, with the support of custom validation and errors.

I'd appreciate if you find a bit of time to try it out and give me feedback before I release 0.5.0 (planning in ~1 week).

See the docs in the Custom validation with a custom error type section.

memark commented 1 month ago

I don't recall being involved in this issue, maybe you meant to tag someone else, @greyblake?

greyblake commented 1 month ago

@memark Sorry my bad.