validation

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 19, 2025 License: MIT Imports: 4 Imported by: 0

README

Validation Package

The validation package provides utilities for validating data in Go applications. It offers a structured way to perform validations, collect validation errors, and report them in a consistent format. This package is useful for validating user input, request data, or any other data that needs to conform to certain rules or constraints.

Features

  • Structured Validation: Collect and report validation errors in a structured way
  • Common Validators: Built-in validators for common validation scenarios
  • Generic Support: Type-safe validation with Go generics
  • Composable Validation: Combine multiple validators for complex validation rules
  • Field-Level Validation: Validate individual fields with specific error messages
  • Collection Validation: Validate all items in a collection

Installation

go get github.com/abitofhelp/servicelib/validation

Usage

Basic Validation
package main

import (
    "fmt"
    
    "github.com/abitofhelp/servicelib/validation"
)

func main() {
    // Create a validation result
    result := validation.NewValidationResult()
    
    // Validate a username
    username := "john"
    validation.Required(username, "username", result)
    validation.MinLength(username, 5, "username", result)
    validation.MaxLength(username, 50, "username", result)
    
    // Validate an email
    email := "john@example.com"
    validation.Required(email, "email", result)
    validation.Pattern(email, `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`, "email", result)
    
    // Check if validation passed
    if !result.IsValid() {
        fmt.Printf("Validation failed: %v\n", result.Error())
        return
    }
    
    fmt.Println("Validation passed!")
}
Date Validation
package main

import (
    "fmt"
    "time"
    
    "github.com/abitofhelp/servicelib/validation"
)

func main() {
    // Create a validation result
    result := validation.NewValidationResult()
    
    // Validate a birth date (must be in the past)
    birthDate := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
    validation.PastDate(birthDate, "birthDate", result)
    
    // Validate a date range
    startDate := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
    endDate := time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC)
    validation.ValidDateRange(startDate, endDate, "startDate", "endDate", result)
    
    // Check if validation passed
    if !result.IsValid() {
        fmt.Printf("Validation failed: %v\n", result.Error())
        return
    }
    
    fmt.Println("Validation passed!")
}
Collection Validation
package main

import (
    "fmt"
    
    "github.com/abitofhelp/servicelib/validation"
)

func main() {
    // Create a validation result
    result := validation.NewValidationResult()
    
    // Validate a slice of strings (all must be non-empty)
    tags := []string{"tag1", "tag2", ""}
    
    // Using AllTrue
    allNonEmpty := validation.AllTrue(tags, func(tag string) bool {
        return tag != ""
    })
    
    if !allNonEmpty {
        result.AddError("all tags must be non-empty", "tags")
    }
    
    // Using ValidateAll
    validation.ValidateAll(tags, func(tag string, index int, result *validation.ValidationResult) {
        if tag == "" {
            result.AddError(fmt.Sprintf("tag at index %d is empty", index), "tags")
        }
    }, result)
    
    // Check if validation passed
    if !result.IsValid() {
        fmt.Printf("Validation failed: %v\n", result.Error())
        return
    }
    
    fmt.Println("Validation passed!")
}
Custom Validation
package main

import (
    "fmt"
    
    "github.com/abitofhelp/servicelib/validation"
)

// User represents a user in the system
type User struct {
    ID       string
    Username string
    Email    string
    Age      int
    Role     string
}

// ValidateUser validates a user
func ValidateUser(user User) error {
    result := validation.NewValidationResult()
    
    // Validate ID
    validation.ValidateID(user.ID, "id", result)
    
    // Validate username
    validation.Required(user.Username, "username", result)
    validation.MinLength(user.Username, 3, "username", result)
    validation.MaxLength(user.Username, 50, "username", result)
    
    // Validate email
    validation.Required(user.Email, "email", result)
    validation.Pattern(user.Email, `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`, "email", result)
    
    // Validate age
    if user.Age < 18 {
        result.AddError("must be at least 18 years old", "age")
    }
    
    // Validate role
    if user.Role != "admin" && user.Role != "user" && user.Role != "guest" {
        result.AddError("must be one of: admin, user, guest", "role")
    }
    
    return result.Error()
}

func main() {
    // Create a user
    user := User{
        ID:       "123",
        Username: "jo", // Too short
        Email:    "invalid-email", // Invalid format
        Age:      16, // Too young
        Role:     "superuser", // Invalid role
    }
    
    // Validate the user
    if err := ValidateUser(user); err != nil {
        fmt.Printf("User validation failed: %v\n", err)
        return
    }
    
    fmt.Println("User validation passed!")
}

Best Practices

  1. Create a Validation Result Early: Create a validation result at the beginning of your validation function and pass it to all validators.

    func ValidateRequest(req Request) error {
        result := validation.NewValidationResult()
    
        validation.Required(req.Name, "name", result)
        validation.Required(req.Email, "email", result)
    
        return result.Error()
    }
    
  2. Use Field Names Consistently: Use consistent field names across your application to make error messages more understandable.

    // Good - consistent field names
    validation.Required(user.FirstName, "firstName", result)
    validation.Required(user.LastName, "lastName", result)
    
    // Avoid - inconsistent field names
    validation.Required(user.FirstName, "first_name", result)
    validation.Required(user.LastName, "last", result)
    
  3. Combine Validators: Combine multiple validators for complex validation rules.

    // Validate a password
    validation.Required(password, "password", result)
    validation.MinLength(password, 8, "password", result)
    validation.Pattern(password, `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$`, "password", result)
    
  4. Validate Collections Properly: Use ValidateAll for collections where each item needs validation.

    // Validate a list of addresses
    validation.ValidateAll(user.Addresses, func(address Address, index int, result *validation.ValidationResult) {
        validation.Required(address.Street, fmt.Sprintf("addresses[%d].street", index), result)
        validation.Required(address.City, fmt.Sprintf("addresses[%d].city", index), result)
        validation.Required(address.Country, fmt.Sprintf("addresses[%d].country", index), result)
    }, result)
    
  5. Return Early for Critical Validations: For performance reasons, return early if critical validations fail.

    func ValidateOrder(order Order) error {
        result := validation.NewValidationResult()
    
        // Critical validation - return early if ID is missing
        if order.ID == "" {
            result.AddError("is required", "id")
            return result.Error()
        }
    
        // Continue with other validations
        validation.Required(order.CustomerID, "customerId", result)
        // ...
    
        return result.Error()
    }
    

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AllTrue

func AllTrue[T any](items []T, predicate func(T) bool) bool

AllTrue validates that all items in a slice satisfy a predicate

func MaxLength

func MaxLength(value string, max int, field string, result *ValidationResult)

MaxLength validates that a string has a maximum length

func MinLength

func MinLength(value string, min int, field string, result *ValidationResult)

MinLength validates that a string has a minimum length

func PastDate

func PastDate(value time.Time, field string, result *ValidationResult)

PastDate validates that a date is in the past

func Pattern

func Pattern(value, pattern, field string, result *ValidationResult)

Pattern validates that a string matches a regular expression

func Required

func Required(value, field string, result *ValidationResult)

Required validates that a string is not empty

func ValidDateRange

func ValidDateRange(start, end time.Time, startField, endField string, result *ValidationResult)

ValidDateRange validates that a start date is before an end date

func ValidateAll

func ValidateAll[T any](items []T, validator func(T, int, *ValidationResult), result *ValidationResult)

ValidateAll validates all items in a slice

func ValidateID

func ValidateID(id, field string, result *ValidationResult)

ValidateID validates that an ID is not empty

Types

type ValidationResult

type ValidationResult struct {
	// contains filtered or unexported fields
}

ValidationResult holds the result of a validation operation

func NewValidationResult

func NewValidationResult() *ValidationResult

NewValidationResult creates a new ValidationResult

func (*ValidationResult) AddError

func (v *ValidationResult) AddError(msg, field string)

AddError adds an error to the validation result

func (*ValidationResult) Error

func (v *ValidationResult) Error() error

Error returns the validation errors as an error

func (*ValidationResult) IsValid

func (v *ValidationResult) IsValid() bool

IsValid returns true if there are no validation errors

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL