GitHub - cinar/checker: Effortless input validation in Go with the power of struct tags. No dependencies, just pure simplicity. ✨ See how! πŸ‘€ (original) (raw)

GoDoc License Go Report Card Go CI codecov

Checker

Checker is a lightweight Go library designed to validate user input efficiently. It supports validation of both struct fields and individual input values.

While there are numerous validation libraries available, Checker stands out due to its simplicity and lack of external dependencies. This makes it an ideal choice for developers who prefer to minimize dependencies and maintain control over their tools. Checker is straightforward to use and effectively meets your validation needs.

Usage

To begin using the Checker library, install it with the following command:

go get github.com/cinar/checker/v2

Then, import the library into your source file as shown below:

import ( checker "github.com/cinar/checker/v2" )

Validating User Input Stored in a Struct

Checker can validate user input stored in a struct by listing the checkers in the struct tags for each field. Here is an example:

type Person struct { Name string checkers:"trim required" }

person := &Person{ Name: " Onur Cinar ", }

errors, valid := checker.CheckStruct(person) if !valid { // Handle validation errors }

Validating Individual User Input with Multiple Checkers

You can also validate individual user input by calling checker functions directly. Here is an example:

name := " Onur Cinar "

name, err := checker.Check(name, checker.Trim, checker.Required) if err != nil { // Handle validation error }

The checkers and normalizers can also be provided through a config string. Here is an example:

name := " Onur Cinar "

name, err := checker.CheckWithConfig(name, "trim requied") if err != nil { // Handle validation error }

Validating Individual User Input

For simpler validation, you can call individual checker functions. Here is an example:

name := "Onur Cinar"

err := checker.IsRequired(name) if err != nil { // Handle validation error }

Normalizers and Checkers

Checkers validate user input, while normalizers transform it into a preferred format. For example, a normalizer can trim spaces from a string or convert it to title case.

Although combining checkers and normalizers into a single library might seem unconventional, using them together can be beneficial. They can be mixed in any order when defining validation steps. For instance, you can use the trim normalizer with the required checker to first trim the input and then ensure it is provided. Here is an example:

type Person struct { Name string checkers:"trim required" }

Checkers Provided

Normalizers Provided

Custom Checkers and Normalizers

You can define custom checkers or normalizers and register them for use in your validation logic. Here is an example of how to create and register a custom checker:

checker.RegisterMaker("is-fruit", func(params string) v2.CheckFunc[reflect.Value] { return func(value reflect.Value) (reflect.Value, error) { stringValue := value.Interface().(string)

    if stringValue == "apple" || stringValue == "banana" {
        return value, nil
    }

    return value, v2.NewCheckError("NOT_FRUIT")
}

})

In this example, the custom checker is-fruit checks if the input value is either "apple" or "banana". If the value is not one of these, it returns an error.

Once registered, you can use your custom checker in struct tags just like the built-in checkers:

type Item struct { Name string checkers:"is-fruit" }

item := &Item{ Name: "banana", }

errors, valid := v2.CheckStruct(item) if !valid { fmt.Println(errors) }

In this example, the is-fruit checker is used to validate that the Name field of the Item struct is either "apple" or "banana".

Slice and Item Level Checkers

When adding checker struct tags to a slice, you can use the @ prefix to indicate that the checker should be applied to the slice itself. Checkers without the @ prefix will be applied to the individual items within the slice. Here is an example:

type Person struct { Name string checkers:"required" Emails []string checkers:"@max-len:2 max-len:64" }

In this example:

Localized Error Messages

When validation fails, Checker returns an error. By default, the Error() function provides a human-readable error message in en-US locale.

_, err := checker.IsEmail("abcd") if err != nil { fmt.Println(err) // Output: Not a valid email address. }

To get error messages in other languages, use the ErrorWithLocale() function. By default, only en-US is registered. You can register additional languages by calling RegisterLocale.

// Register de-DE localized error messages. checker.RegisterLocale(locales.DeDE, locales.DeDEMessages)

_, err := checker.IsEmail("abcd") if err != nil { fmt.Println(err.ErrorWithLocale(locales.DeDE)) // Output: Keine gΓΌltige E-Mail-Adresse. }

You can also customize existing error messages or add new ones to locales.EnUSMessages and other locale maps.

// Register the en-US localized error message for the custom NOT_FRUIT error code. locales.EnUSMessages["NOT_FRUIT"] = "Not a fruit name."

errors, valid := v2.CheckStruct(item) if !valid { fmt.Println(errors) // Output: map[Name:Not a fruit name.] }

Error messages are generated using Golang template functions, allowing them to include variables.

// Custrom checker error containing the item name. err := checker.NewCheckErrorWithData( "NOT_FRUIT", map[string]interface{}{ "name": "abcd", }, )

// Register the en-US localized error message for the custom NOT_FRUIT error code. locales.EnUSMessages["NOT_FRUIT"] = "Name {{ .name }} is not a fruit name."

errors, valid := v2.CheckStruct(item) if !valid { fmt.Println(errors) // Output: map[Name:Name abcd is not a fruit name.] }

Contributing to the Project

Anyone can contribute to Checkers library. Please make sure to read our Contributor Covenant Code of Conduct guide first. Follow the How to Contribute to Checker to contribute.

License

This library is free to use, modify, and distribute under the terms of the MIT license. The full license text can be found in the LICENSE file.

The MIT license is a permissive license that allows you to do almost anything with the library, as long as you retain the copyright notice and the license text. This means that you can use the library in commercial products, modify it, and redistribute it without having to ask for permission from the authors.

The LICENSE file is located in the root directory of the library. You can open it in a text editor to read the full license text.