Purify is a Go library for struct validation based on tags. It allows you to easily validate data within structs using purify tags, making your code cleaner and easier to maintain.
BSD-3-CLAUSE License
Purify is a Go library for struct validation based on tags. It allows you to easily validate data within structs using purify
tags, making your code cleaner and easier to maintain.
Install the library using go get
:
go get github.com/contributors-company/purify
Import the package into your project:
import "github.com/contributors-company/purify"
Purify allows you to validate struct fields using purify
tags in your struct definitions.
package main
import (
"fmt"
"github.com/contributors-company/purify"
)
type User struct {
Email string `json:"email" purify:"required|email"`
Username string `json:"username" purify:"required|min(3)|max(20)"`
Age int `json:"age" purify:"min(18)|max(99)"`
}
func main() {
user := User{
Email: "[email protected]",
Username: "example_user",
Age: 25,
}
// Validate the struct
err := purify.ValidateStruct(user)
if err != nil {
fmt.Println("Validation error:", err)
} else {
fmt.Println("Validation successful")
}
}
In this example:
Email
field must be filled and contain a valid email address.Username
field must be filled, have a length of at least 3 and at most 20 characters.Age
field must be at least 18 and at most 99.required
checks that the field is not empty.email
checks that the field contains a valid email address.min(n)
checks that the field's value is not less than n
. For strings, this is the length of the string; for numbers, the value itself.max(n)
checks that the field's value is not greater than n
. For strings, this is the length of the string; for numbers, the value itself.Example of tag usage:
type Product struct {
Name string `json:"name" purify:"required|min(3)|max(50)"`
Price float64 `json:"price" purify:"required|min(0.01)"`
}
Purify provides the ability to create your own validators to extend the library's functionality to meet your project's specific requirements.
To create a custom validator, define a function that matches the ValidatorFunc
type and register it using RegisterValidator
.
ValidatorFunc
type:
type ValidatorFunc func(fieldValue string, param string) string
fieldValue
the value of the field to be validated.param
the parameter passed in the validation tag (if any).Example of custom validators:
func Min() ValidatorFunc {
return func(fieldValue string, param string) string {
minLength, _ := strconv.Atoi(param)
if len(fieldValue) < minLength {
return fmt.Sprintf("minimum length is %s characters", param)
}
return ""
}
}
func Max() ValidatorFunc {
return func(fieldValue string, param string) string {
maxLength, _ := strconv.Atoi(param)
if len(fieldValue) > maxLength {
return fmt.Sprintf("maximum length is %s characters", param)
}
return ""
}
}
func Required() ValidatorFunc {
return func(fieldValue string, _ string) string {
if fieldValue == "" {
return "required field"
}
return ""
}
}
func Email() ValidatorFunc {
return func(fieldValue string, _ string) string {
emailRegex := `^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`
matched, _ := regexp.MatchString(emailRegex, fieldValue)
if !matched {
return "invalid email"
}
return ""
}
}
// Register the validators
func init() {
RegisterValidator("min", Min())
RegisterValidator("max", Max())
RegisterValidator("required", Required())
RegisterValidator("email", Email())
}
Suppose you want to add password validation to meet certain requirements, such as:
Creating a custom validator for password:
func Password() ValidatorFunc {
return func(fieldValue string, _ string) string {
if len(fieldValue) < 8 {
return "password must be at least 8 characters long"
}
if !regexp.MustCompile(`[0-9]`).MatchString(fieldValue) {
return "password must contain at least one digit"
}
if !regexp.MustCompile(`[A-Z]`).MatchString(fieldValue) {
return "password must contain at least one uppercase letter"
}
return ""
}
}
// Register the validator
func init() {
RegisterValidator("password", Password())
}
Using the custom validator in a struct:
type Credentials struct {
Password string `json:"password" purify:"required|password"`
}
Usage example:
package main
import (
"fmt"
"github.com/contributors-company/purify"
)
type Credentials struct {
Password string `json:"password" purify:"required|password"`
}
func main() {
creds := Credentials{
Password: "Passw1", // Invalid password
}
// Validate the struct
err := purify.ValidateStruct(creds)
if err != nil {
validationErrors := err.(purify.ValidationError)
for field, errors := range validationErrors.Errors {
fmt.Printf("Field '%s' has the following errors:\n", field)
for _, errMsg := range errors {
fmt.Printf("- %s\n", errMsg)
}
}
} else {
fmt.Println("Validation successful")
}
}
Expected output:
Field 'Password' has the following errors:
- password must be at least 8 characters long
- password must contain at least one uppercase letter
The ValidateStruct
function returns nil
if there are no validation errors or an error object with the following structure:
{
"errors": {
"field_name": ["error_message1", "error_message2"],
"another_field": ["error_message"]
},
"message": "General error message"
}
Example of error handling:
err := purify.ValidateStruct(user)
if err != nil {
validationErrors := err.(purify.ValidationError)
for field, errors := range validationErrors.Errors {
fmt.Printf("Field '%s' has the following errors:\n", field)
for _, errMsg := range errors {
fmt.Printf("- %s\n", errMsg)
}
}
} else {
fmt.Println("Validation successful")
}
You can run tests using the command:
go test
Sample test from the library:
package purify
import (
"testing"
)
type TestStruct struct {
Name string `json:"name" purify:"required|min(3)|max(20)"`
Email string `json:"email" purify:"required|email"`
Password string `json:"password" purify:"required|password"`
}
func TestValidator(t *testing.T) {
s := TestStruct{
Name: "Al",
Email: "invalid_email",
Password: "pass",
}
// Validate the struct
err := ValidateStruct(s)
if err == nil {
t.Errorf("Expected validation error, got nil")
} else {
validationErrors := err.(ValidationError)
if len(validationErrors.Errors) != 3 {
t.Errorf("Expected 3 validation errors, got %d", len(validationErrors.Errors))
}
}
}
This project is licensed under the MIT License - see the LICENSE file for details.