anthdm / superkit

MIT License
993 stars 84 forks source link

validation package rewrite #21

Closed Oudwins closed 3 months ago

Oudwins commented 3 months ago

This is the rewrite of the validation package I mentioned in #13.

I opened a new PR so you can view the approach. If you think its correct we can continue with it in this style. If not I am happy to just contribute the env parser and we can forget about this approach to validation.

However I do think it has quite a lot of advantages Here is an example schema

    schema := Schema{
               // I have added support for slices
        "emails":     Slice(String().Email()).Min(1),
        "name":      String().Min(3).Max(10),
        "age":       Int().GT(18),
        "isMarried": Bool().True(),
        "lights":    Bool().True(),
                 // each data type has typesafe methods. You can't add email validation to a float for example
        "cash":      Float().GT(10.0),
        // refine allows the implementation of a custom rule by just providing a rule name, error message and a func
        "swagger": String().Refine("swagger", "should be doweird", func(rule p.Rule) bool {
            return rule.FieldValue.(string) == "doweird"
        }),
    }

I won't say the approach is perfect, it does have some trade-offs:

  1. I have had to write quite a lot more code. Since each type has its own struct with its own methods. A lot of the checking logic is reused in but its still more code
  2. For optional overriding of default validation messages (not yet implemented) we have to do something like this which is a little nasty
func (v *StringValidator) Min(n int, msg ...string) *StringValidator {
    defaultMsg := fmt.Sprintf("should be at least %d characters long", n)
    if len(msg) > 0 {
        defaultMsg = msg[0]
    }
    v.Rules = append(v.Rules,
        p.LenMin[string](n, defaultMsg))
    return v
}

For more examples of validation I suggest you look at the tests. I have also implemented a requestParams validator for example.

WARNING: code won't compile because I have not changed every instance of the use of the previous methods. If you decide to move forward with this implementation I'll make a quick update to the PR

Oudwins commented 3 months ago

I am going to close this for now as I realised there were quite a lot of problems with the architecture and I am modifying it quite a bit. I'll bring it back once I think its in a more stable state