Skip to content

Core API Reference

This reference documents the core types and functions that make up the Souuup validation framework. These are the building blocks you’ll use to create validation schemas and handle errors.

The fundamental type for validation rules. A rule is a function that takes a field state and returns an error if validation fails.

type Rule[T any] func(FieldState[T]) error

Usage:

// Custom rule example
func CustomRule(fs u.FieldState[string]) error {
if fs.Value == "forbidden" {
return fmt.Errorf("value is not allowed")
}
return nil
}
// Using the rule
field := u.Field("hello", CustomRule)

Contains the value being validated and provides context to validation rules.

type FieldState[T any] struct {
Value T
// ... internal fields
}

Properties:

  • Value - The actual value being validated

Usage:

func MyRule(fs u.FieldState[string]) error {
value := fs.Value // Access the value
// Perform validation logic
return nil
}

Represents a field with its value and associated validation rules. Created using the Field function.

type FieldDef[T any] struct {
// ... internal fields
}

Interface: Implements Validable


A map that defines the structure of data to be validated. Can contain both fields and nested schemas.

type Schema map[FieldTag]Validable

Usage:

schema := u.Schema{
"username": u.Field("johndoe", r.MinS(3)),
"profile": u.Schema{
"age": u.Field(25, r.MinN(18)),
},
}

Interface: Implements Validable


The main validator instance that executes validation against a schema.

type Souuup struct {
// ... internal fields
}

Represents validation errors in a hierarchical structure that matches your data schema.

type ValidationError struct {
Errors FieldsErrorMap
NestedErrors NestedErrorsMap
Parent *ValidationError
}

Interface: Implements error

u.Field[T any](value T, rules ...Rule[T]) *FieldDef[T]

Section titled “u.Field[T any](value T, rules ...Rule[T]) *FieldDef[T]”

Creates a validatable field with a value and optional validation rules.

// Simple field
nameField := u.Field("John Doe")
// Field with validation rules
emailField := u.Field("[email protected]", r.NotZero, r.ContainsS("@"))
// Field with custom rule
ageField := u.Field(25, r.MinN(18), func(fs u.FieldState[int]) error {
if fs.Value > 100 {
return fmt.Errorf("age seems unrealistic")
}
return nil
})

Parameters:

  • value - The value to validate
  • rules - Zero or more validation rules to apply

Returns: A field definition that can be used in schemas

Type Safety: The rules must match the type of the value (enforced at compile time)


Creates a new validator instance with the provided schema.

schema := u.Schema{
"username": u.Field("johndoe", r.MinS(3)),
"age": u.Field(25, r.MinN(18)),
}
validator := u.NewSouuup(schema)

Parameters:

  • schema - The validation schema to use

Returns: A validator instance ready for validation


Executes validation against the schema and returns an error if validation fails.

err := validator.Validate()
if err != nil {
// Handle validation errors
fmt.Printf("Validation failed: %s", err.Error())
}

Returns:

  • nil if validation succeeds
  • *ValidationError if validation fails

Usage Pattern:

s := u.NewSouuup(schema)
if err := s.Validate(); err != nil {
// Validation failed
if validationErr, ok := err.(*u.ValidationError); ok {
// Access structured error data
errorMap := validationErr.ToMap()
}
} else {
// Validation succeeded
}

Returns a JSON string representation of the validation errors.

err := validator.Validate()
if err != nil {
jsonStr := err.Error()
// Output: {"field": {"errors": ["error message"]}}
}

Converts the validation error to a map structure for easier programmatic access.

type ToMapResult map[FieldTag]map[string]any
if err := validator.Validate(); err != nil {
if validationErr, ok := err.(*u.ValidationError); ok {
errorMap := validationErr.ToMap()
// Check for specific field errors
if userErrors, exists := errorMap["username"]; exists {
if errors, exists := userErrors["errors"]; exists {
fmt.Printf("Username errors: %v", errors)
}
}
}
}

Returns true if there are any validation errors at any level in the error tree.

if validationErr.HasErrors() {
// There are validation errors
}

(ve *ValidationError) AddError(tag FieldTag, err error)

Section titled “(ve *ValidationError) AddError(tag FieldTag, err error)”

Adds a validation error for a specific field. Primarily used internally and in custom validation logic.

ve := u.NewValidationError()
ve.AddError("username", fmt.Errorf("username is taken"))

Creates a new empty ValidationError instance.

ve := u.NewValidationError()

The interface that all validatable entities must implement.

type Validable interface {
Validate(*ValidationError, FieldTag)
Errors() *ValidationError
}

Implementations:

  • FieldDef[T] - Individual fields
  • Schema - Nested schemas

Methods:

Validates the entity and adds any errors to the provided ValidationError under the given tag.

Returns validation errors for this entity.

// For string validation
type StringRule = Rule[string]
// For numeric validation (integers and floats)
type NumericRule[T Numeric] = Rule[T]
// For slice validation
type SliceRule[T any] = Rule[[]T]
// Field identifier
type FieldTag = string
// Collection of rule errors for a field
type RuleErrors = []RuleError
// Map of field errors
type FieldsErrorMap = map[FieldTag]RuleErrors
// Map of nested errors
type NestedErrorsMap = map[FieldTag]*ValidationError

A type constraint for numeric types (integers and floats).

type Numeric interface {
constraints.Float | constraints.Integer
}

Supported Types:

  • All integer types (int, int8, int16, int32, int64)
  • All unsigned integer types (uint, uint8, uint16, uint32, uint64)
  • All float types (float32, float64)
// 1. Define your data structure
type User struct {
Name string
Email string
Age int
}
// 2. Create validation schema
func ValidateUser(user User) error {
schema := u.Schema{
"name": u.Field(user.Name, r.NotZero, r.MinS(2)),
"email": u.Field(user.Email, r.NotZero, r.ContainsS("@")),
"age": u.Field(user.Age, r.MinN(18), r.MaxN(120)),
}
// 3. Create validator and validate
return u.NewSouuup(schema).Validate()
}
// 4. Use in your application
user := User{Name: "John", Email: "[email protected]", Age: 25}
if err := ValidateUser(user); err != nil {
// Handle validation error
}
func CreateUserSchema(user User) u.Schema {
return u.Schema{
"personal": u.Schema{
"name": u.Field(user.Name, r.NotZero),
"age": u.Field(user.Age, r.MinN(18)),
},
"contact": u.Schema{
"email": u.Field(user.Email, r.NotZero),
"phone": u.Field(user.Phone, r.MinS(10)),
},
}
}
func HandleValidationError(err error) {
if validationErr, ok := err.(*u.ValidationError); ok {
errorMap := validationErr.ToMap()
for field, fieldErrors := range errorMap {
if errors, exists := fieldErrors["errors"]; exists {
fmt.Printf("Field %s: %v\n", field, errors)
}
// Handle nested errors recursively
for nestedField, nestedData := range fieldErrors {
if nestedField != "errors" {
fmt.Printf("Nested field %s.%s has errors\n", field, nestedField)
}
}
}
}
}

Type Safety First

Let the type system catch validation rule mismatches at compile time.

// Correct - types match
u.Field("string", r.MinS(3))
u.Field(42, r.MinN(0))
// Won't compile - type mismatch
u.Field("string", r.MinN(3)) // ❌

Schema Organization

Structure your schemas to mirror your data for clarity.

u.Schema{
"user": u.Schema{
"profile": u.Schema{
"name": u.Field(...),
},
},
}

Error Handling

Always check validation results and handle errors appropriately.

if err := validator.Validate(); err != nil {
// Handle the error - don't ignore it
return fmt.Errorf("validation failed: %w", err)
}

Reusable Schemas

Create reusable validation functions for common patterns.

func PersonSchema(person Person) u.Schema {
return u.Schema{
"name": u.Field(person.Name, r.NotZero),
"age": u.Field(person.Age, r.MinN(0)),
}
}

Built-in Rules

Explore all the built-in validation rules available in Souuup.

Rules Reference →

Examples

See practical examples of using the core API.

Examples →