poxxy

package module
v0.0.0-...-5c66fc9 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2025 License: MIT Imports: 13 Imported by: 0

README

Poxxy

A powerful Go library for data validation, transformation, and schema definition with support for default values and integrated transformers.

Features

  • Type-safe schema definition with generics
  • Built-in validators for common validation rules
  • Default values for fields when not provided
  • Integrated transformers for data sanitization and formatting
  • Support for complex types including structs, slices, arrays, pointers, maps, and unions
  • HTTP request validation with automatic content-type detection
  • Comprehensive error handling with detailed validation messages
  • Two-phase validation (assignment then validation) for cross-field validation

Quick Start

package main

import (
    "fmt"
    "time"
    "github.com/arkan/poxxy"
)

func main() {
    var name string
    var email string
    var age int
    var createdAt time.Time

    schema := poxxy.NewSchema(
        poxxy.Value("name", &name,
            poxxy.WithDefault("Anonymous"),
            poxxy.WithTransformers(
                poxxy.TrimSpace(),
                poxxy.Capitalize(),
            ),
            poxxy.WithValidators(poxxy.Required()),
        ),
        poxxy.Value("email", &email,
            poxxy.WithTransformers(poxxy.SanitizeEmail()),
            poxxy.WithValidators(poxxy.Required(), poxxy.Email()),
        ),
        poxxy.Value("age", &age,
            poxxy.WithDefault(25),
            poxxy.WithValidators(poxxy.Min(18), poxxy.Max(120)),
        ),
        poxxy.Convert("created_at", &createdAt, func(dateStr string) (time.Time, error) {
            return time.Parse("2006-01-02", dateStr)
        }, poxxy.WithDefault(time.Now())),
    )

    data := map[string]interface{}{
        "name":  "  john doe  ",
        "email": "  JOHN.DOE@EXAMPLE.COM  ",
        "created_at": "2024-01-15",
        // age will use default value
    }

    if err := schema.Apply(data); err != nil {
        fmt.Printf("Validation failed: %v\n", err)
        return
    }

    fmt.Printf("Name: '%s'\n", name)   // Output: Name: 'John doe'
    fmt.Printf("Email: '%s'\n", email) // Output: Email: 'john.doe@example.com'
    fmt.Printf("Age: %d\n", age)       // Output: Age: 25
    fmt.Printf("Created At: %v\n", createdAt) // Output: Created At: 2024-01-15 00:00:00 +0000 UTC
}

Field Types

Value Fields

Basic value fields for simple types like string, int, bool, etc.

var name string
poxxy.Value("name", &name, opts...)
Pointer Fields

Pointer fields for optional values or complex structs.

var user *User
poxxy.Pointer("user", &user, opts...)
Slice Fields

Slice fields for arrays of values or structs.

var tags []string
poxxy.Slice("tags", &tags, opts...)
Array Fields

Fixed-size array fields.

var coords [2]float64
poxxy.Array("coords", &coords, opts...)
Map Fields

Map fields for key-value pairs with validation.

var settings map[string]string
poxxy.Map("settings", &settings, opts...)

// With default values
defaultSettings := map[string]string{
    "theme": "dark",
    "lang":  "en",
}
poxxy.Map("settings", &settings, poxxy.WithDefault(defaultSettings), opts...)

Advanced Field Types

HTTPMap Fields - HTTP Form Data Management

HTTPMap fields are specifically designed to handle complex HTTP form data where each map value is a structure with its own validation schema. This functionality is particularly useful for web forms with object collections.

Working with Form Data

HTTPMap fields automatically parse form data in the format field[key][subfield]=value and convert them to typed structures.

type Attachment struct {
    URL      string `json:"url"`
    Filename string `json:"filename"`
    Size     int    `json:"size"`
}

var attachments map[string]Attachment
defaultAttachments := map[string]Attachment{
    "default": {
        URL:      "https://example.com/default.pdf",
        Filename: "default.pdf",
        Size:     1024,
    },
}

schema := poxxy.NewSchema(
    poxxy.HTTPMap("attachments", &attachments, func(s *poxxy.Schema, a *Attachment) {
        poxxy.WithSchema(s, poxxy.Value("url", &a.URL,
            poxxy.WithValidators(poxxy.Required(), poxxy.URL()),
            poxxy.WithDescription("Attachment URL"),
        ))
        poxxy.WithSchema(s, poxxy.Value("filename", &a.Filename,
            poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(1)),
            poxxy.WithTransformers(poxxy.TrimSpace()),
        ))
        poxxy.WithSchema(s, poxxy.Value("size", &a.Size,
            poxxy.WithDefault(0),
            poxxy.WithValidators(poxxy.Min(0)),
        ))
    }, poxxy.WithDefault(defaultAttachments)),
)
Form Data Handling

This schema can handle form data like:

attachments[0][url]=https://example.com/doc1.pdf&attachments[0][filename]=doc1.pdf&attachments[0][size]=2048
attachments[1][url]=https://example.com/doc2.pdf&attachments[1][filename]=doc2.pdf&attachments[1][size]=3072
Complete Example with HTTP Validation
func handleFileUpload(w http.ResponseWriter, r *http.Request) {
    var attachments map[string]Attachment

    schema := poxxy.NewSchema(
        poxxy.HTTPMap("attachments", &attachments, func(s *poxxy.Schema, a *Attachment) {
            poxxy.WithSchema(s, poxxy.Value("url", &a.URL,
                poxxy.WithValidators(poxxy.Required(), poxxy.URL()),
            ))
            poxxy.WithSchema(s, poxxy.Value("filename", &a.Filename,
                poxxy.WithValidators(poxxy.Required()),
                poxxy.WithTransformers(poxxy.TrimSpace()),
            ))
            poxxy.WithSchema(s, poxxy.Value("size", &a.Size,
                poxxy.WithValidators(poxxy.Min(1), poxxy.Max(10*1024*1024)), // Max 10MB
            ))
        }),
    )

    if err := schema.ApplyHTTPRequest(r); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Process validated attachments
    for key, attachment := range attachments {
        fmt.Printf("Attachment %s: %s (%d bytes)\n", key, attachment.Filename, attachment.Size)
    }
}
Advanced Use Cases

Multiple User Management:

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

var users map[int]User
schema := poxxy.NewSchema(
    poxxy.HTTPMap("users", &users, func(s *poxxy.Schema, u *User) {
        poxxy.WithSchema(s, poxxy.Value("name", &u.Name,
            poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(2)),
            poxxy.WithTransformers(poxxy.TrimSpace(), poxxy.Capitalize()),
        ))
        poxxy.WithSchema(s, poxxy.Value("email", &u.Email,
            poxxy.WithValidators(poxxy.Required(), poxxy.Email()),
            poxxy.WithTransformers(poxxy.SanitizeEmail()),
        ))
        poxxy.WithSchema(s, poxxy.Value("age", &u.Age,
            poxxy.WithValidators(poxxy.Min(18), poxxy.Max(120)),
        ))
    }),
)
NestedMap Fields - Nested Map Validation

NestedMap fields allow you to validate each key-value pair of a map with custom rules. Unlike HTTPMap fields that handle structures, NestedMap fields work with simple values.

Main Use Cases

Configuration Parameters Validation:

var config map[string]string
schema := poxxy.NewSchema(
    poxxy.NestedMap("config", &config, func(s *poxxy.Schema, key string, value *string) {
        // Key validation
        poxxy.WithSchema(s, poxxy.ValueWithoutAssign("key",
            poxxy.WithValidators(
                poxxy.Required(),
                poxxy.In("theme", "language", "timezone", "currency"),
            ),
        ))
        // Value validation
        poxxy.WithSchema(s, poxxy.ValueWithoutAssign("value",
            poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(1)),
        ))
    }),
)

Metadata Validation:

var metadata map[string]interface{}
schema := poxxy.NewSchema(
    poxxy.NestedMap("metadata", &metadata, func(s *poxxy.Schema, key string, value *interface{}) {
        // Key validation (must be lowercase)
        poxxy.WithSchema(s, poxxy.ValueWithoutAssign("key",
            poxxy.WithValidators(
                poxxy.Required(),
                poxxy.ValidatorFunc(func(key string, fieldName string) error {
                    if key != strings.ToLower(key) {
                        return fmt.Errorf("key must be lowercase")
                    }
                    return nil
                }),
            ),
        ))
        // Value validation (must be string or number)
        poxxy.WithSchema(s, poxxy.ValueWithoutAssign("value",
            poxxy.WithValidators(
                poxxy.Required(),
                poxxy.ValidatorFunc(func(value interface{}, fieldName string) error {
                    switch v := value.(type) {
                    case string, int, float64:
                        return nil
                    default:
                        return fmt.Errorf("value must be string, int, or float, got %T", v)
                    }
                }),
            ),
        ))
    }),
)

Permissions Validation:

var permissions map[string]bool
schema := poxxy.NewSchema(
    poxxy.NestedMap("permissions", &permissions, func(s *poxxy.Schema, key string, value *bool) {
        // Key validation (must be a valid permission name)
        poxxy.WithSchema(s, poxxy.ValueWithoutAssign("key",
            poxxy.WithValidators(
                poxxy.Required(),
                poxxy.ValidatorFunc(func(key string, fieldName string) error {
                    validPermissions := []string{"read", "write", "delete", "admin"}
                    for _, perm := range validPermissions {
                        if key == perm {
                            return nil
                        }
                    }
                    return fmt.Errorf("invalid permission: %s", key)
                }),
            ),
        ))
    }),
)
Union Fields - Polymorphism Patterns

Union fields allow you to handle polymorphic structures where the exact type is determined by a discriminator. This functionality is ideal for APIs that need to handle different types of objects.

Polymorphism Patterns

Polymorphic Documents:

type Document interface {
    GetType() string
}

type TextDocument struct {
    Content   string `json:"content"`
    WordCount int    `json:"word_count"`
    Language  string `json:"language"`
}

type ImageDocument struct {
    URL      string `json:"url"`
    Width    int    `json:"width"`
    Height   int    `json:"height"`
    Format   string `json:"format"`
}

type VideoDocument struct {
    URL       string `json:"url"`
    Duration  int    `json:"duration"`
    Quality   string `json:"quality"`
    Subtitles bool   `json:"subtitles"`
}

// GetType method implementations
func (t TextDocument) GetType() string { return "text" }
func (i ImageDocument) GetType() string { return "image" }
func (v VideoDocument) GetType() string { return "video" }

var document Document
schema := poxxy.NewSchema(
    poxxy.Union("document", &document, func(data map[string]interface{}) (interface{}, error) {
        docType, ok := data["type"].(string)
        if !ok {
            return nil, fmt.Errorf("missing or invalid document type")
        }

        switch docType {
        case "text":
            var doc TextDocument
            subSchema := poxxy.NewSchema(
                poxxy.Value("content", &doc.Content,
                    poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(10)),
                    poxxy.WithTransformers(poxxy.TrimSpace()),
                ),
                poxxy.Value("word_count", &doc.WordCount,
                    poxxy.WithValidators(poxxy.Min(0)),
                ),
                poxxy.Value("language", &doc.Language,
                    poxxy.WithDefault("en"),
                    poxxy.WithValidators(poxxy.In("en", "fr", "es", "de")),
                ),
            )
            if err := subSchema.Apply(data); err != nil {
                return nil, err
            }
            return doc, nil

        case "image":
            var doc ImageDocument
            subSchema := poxxy.NewSchema(
                poxxy.Value("url", &doc.URL,
                    poxxy.WithValidators(poxxy.Required(), poxxy.URL()),
                ),
                poxxy.Value("width", &doc.Width,
                    poxxy.WithValidators(poxxy.Min(1), poxxy.Max(10000)),
                ),
                poxxy.Value("height", &doc.Height,
                    poxxy.WithValidators(poxxy.Min(1), poxxy.Max(10000)),
                ),
                poxxy.Value("format", &doc.Format,
                    poxxy.WithDefault("jpeg"),
                    poxxy.WithValidators(poxxy.In("jpeg", "png", "gif", "webp")),
                ),
            )
            if err := subSchema.Apply(data); err != nil {
                return nil, err
            }
            return doc, nil

        case "video":
            var doc VideoDocument
            subSchema := poxxy.NewSchema(
                poxxy.Value("url", &doc.URL,
                    poxxy.WithValidators(poxxy.Required(), poxxy.URL()),
                ),
                poxxy.Value("duration", &doc.Duration,
                    poxxy.WithValidators(poxxy.Min(1)),
                ),
                poxxy.Value("quality", &doc.Quality,
                    poxxy.WithDefault("720p"),
                    poxxy.WithValidators(poxxy.In("480p", "720p", "1080p", "4k")),
                ),
                poxxy.Value("subtitles", &doc.Subtitles,
                    poxxy.WithDefault(false),
                ),
            )
            if err := subSchema.Apply(data); err != nil {
                return nil, err
            }
            return doc, nil

        default:
            return nil, fmt.Errorf("unknown document type: %s", docType)
        }
    }),
)

Polymorphic Notifications:

type Notification interface {
    GetChannel() string
}

type EmailNotification struct {
    To      string `json:"to"`
    Subject string `json:"subject"`
    Body    string `json:"body"`
}

type SMSNotification struct {
    Phone   string `json:"phone"`
    Message string `json:"message"`
}

type PushNotification struct {
    Token   string            `json:"token"`
    Title   string            `json:"title"`
    Body    string            `json:"body"`
    Data    map[string]string `json:"data"`
}

func (e EmailNotification) GetChannel() string { return "email" }
func (s SMSNotification) GetChannel() string { return "sms" }
func (p PushNotification) GetChannel() string { return "push" }

var notification Notification
schema := poxxy.NewSchema(
    poxxy.Union("notification", &notification, func(data map[string]interface{}) (interface{}, error) {
        channel, ok := data["channel"].(string)
        if !ok {
            return nil, fmt.Errorf("missing or invalid notification channel")
        }

        switch channel {
        case "email":
            var notif EmailNotification
            subSchema := poxxy.NewSchema(
                poxxy.Value("to", &notif.To,
                    poxxy.WithValidators(poxxy.Required(), poxxy.Email()),
                    poxxy.WithTransformers(poxxy.SanitizeEmail()),
                ),
                poxxy.Value("subject", &notif.Subject,
                    poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(1), poxxy.MaxLength(200)),
                ),
                poxxy.Value("body", &notif.Body,
                    poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(10)),
                ),
            )
            if err := subSchema.Apply(data); err != nil {
                return nil, err
            }
            return notif, nil

        case "sms":
            var notif SMSNotification
            subSchema := poxxy.NewSchema(
                poxxy.Value("phone", &notif.Phone,
                    poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(10)),
                ),
                poxxy.Value("message", &notif.Message,
                    poxxy.WithValidators(poxxy.Required(), poxxy.MaxLength(160)),
                ),
            )
            if err := subSchema.Apply(data); err != nil {
                return nil, err
            }
            return notif, nil

        case "push":
            var notif PushNotification
            subSchema := poxxy.NewSchema(
                poxxy.Value("token", &notif.Token,
                    poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(32)),
                ),
                poxxy.Value("title", &notif.Title,
                    poxxy.WithValidators(poxxy.Required(), poxxy.MaxLength(50)),
                ),
                poxxy.Value("body", &notif.Body,
                    poxxy.WithValidators(poxxy.Required(), poxxy.MaxLength(200)),
                ),
                poxxy.Map("data", &notif.Data),
            )
            if err := subSchema.Apply(data); err != nil {
                return nil, err
            }
            return notif, nil

        default:
            return nil, fmt.Errorf("unknown notification channel: %s", channel)
        }
    }),
)
NestedMap Fields

Nested map fields with validation for each key-value pair.

var userSettings map[string]string
poxxy.NestedMap("settings", &userSettings, func(s *poxxy.Schema, key string, value *string) {
    // Validate each key-value pair
    poxxy.WithSchema(s, poxxy.ValueWithoutAssign("key", poxxy.WithValidators(poxxy.Required())))
    poxxy.WithSchema(s, poxxy.ValueWithoutAssign("value", poxxy.WithValidators(poxxy.Required())))
}, opts...)
Struct Fields

Struct fields for nested object validation with sub-schemas.

type Address struct {
    Street string
    City   string
}

var address Address
poxxy.Struct("address", &address, poxxy.WithSubSchema(func(s *poxxy.Schema, addr *Address) {
    poxxy.WithSchema(s, poxxy.Value("street", &addr.Street, poxxy.WithValidators(poxxy.Required())))
    poxxy.WithSchema(s, poxxy.Value("city", &addr.City, poxxy.WithValidators(poxxy.Required())))
}), opts...)
Convert Fields

Fields that convert from one type to another (e.g., string to time.Time).

var createdAt time.Time
poxxy.Convert("created_at", &createdAt, func(dateStr string) (time.Time, error) {
    return time.Parse("2006-01-02", dateStr)
}, opts...)

var updatedAt *time.Time
    poxxy.ConvertPointer("updated_at", &updatedAt, func(dateStr string) (*time.Time, error) {
        t, err := time.Parse("2006-01-02T15:04:05Z", dateStr)
        if err != nil {
            return nil, err
        }
        return &t, nil
    }, opts...)
ValueWithoutAssign Fields

Fields that validate values without assigning them to variables (useful in map validation).

poxxy.ValueWithoutAssign("key", poxxy.WithValidators(poxxy.Required()))

Options

Default Values

Set default values for fields when they're not provided in the input data.

poxxy.WithDefault("Anonymous")
poxxy.WithDefault(25)
poxxy.WithDefault(true)
poxxy.WithDefault(map[string]string{"theme": "dark"})
Transformers

Transform data before assignment and validation.

Built-in Transformers
  • ToUpper() - Convert string to uppercase
  • ToLower() - Convert string to lowercase
  • TrimSpace() - Remove leading and trailing whitespace
  • TitleCase() - Convert string to title case
  • Capitalize() - Capitalize first letter
  • SanitizeEmail() - Normalize email addresses
Custom Transformers

Custom transformers allow you to create data transformations specific to your business domain.

Basic Transformers:

// Phone number normalization transformer
phoneTransformer := poxxy.CustomTransformer(func(phone string) (string, error) {
    // Remove all non-numeric characters
    cleaned := strings.Map(func(r rune) rune {
        if r >= '0' && r <= '9' {
            return r
        }
        return -1
    }, phone)

    // Check length
    if len(cleaned) < 10 {
        return "", fmt.Errorf("phone number too short")
    }

    // Format as international format
    if len(cleaned) == 10 {
        return "+1" + cleaned, nil
    }

    return "+" + cleaned, nil
})

var phone string
schema := poxxy.NewSchema(
    poxxy.Value("phone", &phone,
        poxxy.WithTransformers(phoneTransformer),
        poxxy.WithValidators(poxxy.Required()),
    ),
)

Business Validation Transformers:

// Age validation transformer with automatic calculation
ageTransformer := poxxy.CustomTransformer(func(birthDate string) (int, error) {
    birth, err := time.Parse("2006-01-02", birthDate)
    if err != nil {
        return 0, fmt.Errorf("invalid birth date format")
    }

    age := time.Now().Year() - birth.Year()
    if time.Now().YearDay() < birth.YearDay() {
        age--
    }

    if age < 0 {
        return 0, fmt.Errorf("birth date cannot be in the future")
    }

    return age, nil
})

var age int
schema := poxxy.NewSchema(
            poxxy.Convert("birth_date", &age, func(birthDate string) (*int, error) {
            age, err := ageTransformer.Transform(birthDate)
            if err != nil {
                return nil, err
            }
            return &age, nil
        }, poxxy.WithValidators(poxxy.Min(18))),
)

Formatting Transformers:

// Currency formatting transformer
currencyTransformer := poxxy.CustomTransformer(func(amount float64) (string, error) {
    if amount < 0 {
        return "", fmt.Errorf("amount cannot be negative")
    }
    return fmt.Sprintf("$%.2f", amount), nil
})

var formattedAmount string
schema := poxxy.NewSchema(
            poxxy.Convert("amount", &formattedAmount, func(amount float64) (*string, error) {
            formatted, err := currencyTransformer.Transform(amount)
            if err != nil {
                return nil, err
            }
            return &formatted, nil
        }),
)

Security Transformers:

// HTML sanitization transformer
htmlSanitizer := poxxy.CustomTransformer(func(content string) (string, error) {
    // Remove dangerous HTML tags
    dangerousTags := []string{"<script>", "</script>", "<iframe>", "</iframe>", "<object>", "</object>"}
    cleaned := content
    for _, tag := range dangerousTags {
        cleaned = strings.ReplaceAll(cleaned, tag, "")
    }

    // Limit length
    if len(cleaned) > 10000 {
        return "", fmt.Errorf("content too long")
    }

    return cleaned, nil
})

var safeContent string
schema := poxxy.NewSchema(
    poxxy.Value("content", &safeContent,
        poxxy.WithTransformers(htmlSanitizer),
        poxxy.WithValidators(poxxy.Required(), poxxy.MinLength(10)),
    ),
)

Normalization Transformers:

// URL normalization transformer
urlNormalizer := poxxy.CustomTransformer(func(url string) (string, error) {
    url = strings.TrimSpace(url)

    // Add protocol if missing
    if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
        url = "https://" + url
    }

    // Normalize to lowercase
    url = strings.ToLower(url)

    return url, nil
})

var normalizedURL string
schema := poxxy.NewSchema(
    poxxy.Value("url", &normalizedURL,
        poxxy.WithTransformers(urlNormalizer),
        poxxy.WithValidators(poxxy.Required(), poxxy.URL()),
    ),
)
Validators

Apply validation rules to fields.

poxxy.WithValidators(
    poxxy.Required(),
    poxxy.Email(),
    poxxy.Min(18),
    poxxy.Max(120),
    poxxy.MinLength(3),
    poxxy.MaxLength(50),
)
Descriptions

Add descriptions to fields for better error messages and documentation.

poxxy.WithDescription("User's full name")
Sub-schema Options

Configure sub-schemas for complex nested structures.

poxxy.WithSubSchema(func(s *poxxy.Schema, user *User) {
    // Configure sub-schema fields
})

poxxy.WithSubSchemaMap(func(s *poxxy.Schema, key string, value *string) {
    // Configure map value validation
})

Built-in Validators

Basic Validators
  • Required() - Field must be present and non-empty
  • NotEmpty() - Value must not be empty (non-zero)
  • Email() - Valid email format
  • URL() - Valid URL format (http/https only)
Numeric Validators
  • Min(value) - Minimum numeric value
  • Max(value) - Maximum numeric value
String and Collection Validators
  • MinLength(length) - Minimum string/slice length
  • MaxLength(length) - Maximum string/slice length
  • In(values...) - Value must be in the provided list
  • Unique() - Slice/array/map elements must be unique
  • UniqueBy(keyExtractor) - Elements must be unique by extracted key
Complex Validations with Each(), Unique(), etc.

Complex validations allow you to validate collections with sophisticated rules.

Each() - Validating Each Element

Email List Validation:

var emails []string
schema := poxxy.NewSchema(
    poxxy.Slice("emails", &emails,
        poxxy.WithValidators(
            poxxy.Required(),
            poxxy.Each(poxxy.Email(), poxxy.MinLength(5)),
            poxxy.Unique(),
        ),
        poxxy.WithTransformers(
            poxxy.CustomTransformer(func(emails []string) ([]string, error) {
                // Normalize all emails
                for i, email := range emails {
                    emails[i] = strings.ToLower(strings.TrimSpace(email))
                }
                return emails, nil
            }),
        ),
    ),
)

User List Validation:

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

var users []User
schema := poxxy.NewSchema(
    poxxy.Slice("users", &users,
        poxxy.WithValidators(
            poxxy.Required(),
            poxxy.Each(
                poxxy.ValidatorFunc(func(user User, fieldName string) error {
                    if user.Name == "" {
                        return fmt.Errorf("name is required")
                    }
                    if user.Email == "" {
                        return fmt.Errorf("email is required")
                    }
                    if user.Age < 18 {
                        return fmt.Errorf("user must be at least 18 years old")
                    }
                    return nil
                }),
            ),
            poxxy.UniqueBy(func(user interface{}) interface{} {
                return user.(User).Email
            }),
        ),
    ),
)

Score Array Validation:

var scores [5]int
schema := poxxy.NewSchema(
    poxxy.Array("scores", &scores,
        poxxy.WithValidators(
            poxxy.Each(poxxy.Min(0), poxxy.Max(100)),
            poxxy.ValidatorFunc(func(scores [5]int, fieldName string) error {
                // Check that at least 3 scores are above 50
                count := 0
                for _, score := range scores {
                    if score > 50 {
                        count++
                    }
                }
                if count < 3 {
                    return fmt.Errorf("at least 3 scores must be above 50")
                }
                return nil
            }),
        ),
    ),
)
Unique() and UniqueBy() - Uniqueness Validation

Simple Uniqueness Validation:

var tags []string
schema := poxxy.NewSchema(
    poxxy.Slice("tags", &tags,
        poxxy.WithValidators(
            poxxy.Required(),
            poxxy.Each(poxxy.MinLength(2), poxxy.MaxLength(20)),
            poxxy.Unique(),
        ),
        poxxy.WithTransformers(
            poxxy.CustomTransformer(func(tags []string) ([]string, error) {
                // Normalize tags (lowercase, no spaces)
                for i, tag := range tags {
                    tags[i] = strings.ToLower(strings.ReplaceAll(tag, " ", "-"))
                }
                return tags, nil
            }),
        ),
    ),
)

Uniqueness Validation by Property:

type Product struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Price float64 `json:"price"`
}

var products []Product
schema := poxxy.NewSchema(
    poxxy.Slice("products", &products,
        poxxy.WithValidators(
            poxxy.Required(),
            poxxy.Each(
                poxxy.ValidatorFunc(func(product Product, fieldName string) error {
                    if product.ID == "" {
                        return fmt.Errorf("product ID is required")
                    }
                    if product.Name == "" {
                        return fmt.Errorf("product name is required")
                    }
                    if product.Price <= 0 {
                        return fmt.Errorf("product price must be positive")
                    }
                    return nil
                }),
            ),
            // Check uniqueness by ID
            poxxy.UniqueBy(func(product interface{}) interface{} {
                return product.(Product).ID
            }),
        ),
    ),
)

Uniqueness Validation in a Map:

var settings map[string]string
schema := poxxy.NewSchema(
    poxxy.Map("settings", &settings,
        poxxy.WithValidators(
            poxxy.Required(),
            poxxy.Unique(), // Check uniqueness of values
            poxxy.WithMapKeys("theme", "language", "timezone"), // Required keys
        ),
    ),
)
Complex Cross-Field Validations

Field Consistency Validation:

type Order struct {
    Items      []OrderItem `json:"items"`
    Total      float64     `json:"total"`
    Currency   string      `json:"currency"`
    Discount   float64     `json:"discount"`
    FinalTotal float64     `json:"final_total"`
}

type OrderItem struct {
    ProductID string  `json:"product_id"`
    Quantity  int     `json:"quantity"`
    Price     float64 `json:"price"`
    Subtotal  float64 `json:"subtotal"`
}

var order Order
schema := poxxy.NewSchema(
    poxxy.Struct("order", &order, poxxy.WithSubSchema(func(s *poxxy.Schema, o *Order) {
        poxxy.WithSchema(s, poxxy.Slice("items", &o.Items,
            poxxy.WithValidators(
                poxxy.Required(),
                poxxy.Each(
                    poxxy.ValidatorFunc(func(item OrderItem, fieldName string) error {
                        if item.ProductID == "" {
                            return fmt.Errorf("product ID is required")
                        }
                        if item.Quantity <= 0 {
                            return fmt.Errorf("quantity must be positive")
                        }
                        if item.Price <= 0 {
                            return fmt.Errorf("price must be positive")
                        }
                        // Check that the subtotal is correct
                        expectedSubtotal := float64(item.Quantity) * item.Price
                        if item.Subtotal != expectedSubtotal {
                            return fmt.Errorf("subtotal mismatch: expected %.2f, got %.2f",
                                expectedSubtotal, item.Subtotal)
                        }
                        return nil
                    }),
                ),
                poxxy.UniqueBy(func(item interface{}) interface{} {
                    return item.(OrderItem).ProductID
                }),
            ),
        ))

        poxxy.WithSchema(s, poxxy.Value("total", &o.Total,
            poxxy.WithValidators(poxxy.Min(0)),
        ))

        poxxy.WithSchema(s, poxxy.Value("currency", &o.Currency,
            poxxy.WithValidators(poxxy.In("USD", "EUR", "GBP")),
        ))

        poxxy.WithSchema(s, poxxy.Value("discount", &o.Discount,
            poxxy.WithValidators(poxxy.Min(0), poxxy.Max(100)),
        ))

        poxxy.WithSchema(s, poxxy.Value("final_total", &o.FinalTotal,
            poxxy.WithValidators(
                poxxy.ValidatorFunc(func(finalTotal float64, fieldName string) error {
                    // Check that the final total is consistent
                    expectedTotal := o.Total * (1 - o.Discount/100)
                    if finalTotal != expectedTotal {
                        return fmt.Errorf("final total mismatch: expected %.2f, got %.2f",
                            expectedTotal, finalTotal)
                    }
                    return nil
                }),
            ),
        ))
    })),
)
Custom Validators
poxxy.ValidatorFunc(func(value string, fieldName string) error {
    if !strings.Contains(value, "@") {
        return fmt.Errorf("must contain @ symbol")
    }
    return nil
})
Validator Messages

Customize error messages for validators.

poxxy.Required().WithMessage("This field is mandatory")
poxxy.Min(18).WithMessage("Must be at least 18 years old")

Schema Options

Skip Validators

Skip validation phase (useful for data transformation only).

schema.Apply(data, poxxy.WithSkipValidators(true))

HTTP Integration

ApplyHTTPRequest

Validate HTTP requests with automatic content-type detection.

func handleRequest(w http.ResponseWriter, r *http.Request) {
    var user User
    schema := poxxy.NewSchema(
        poxxy.Value("name", &user.Name, poxxy.WithValidators(poxxy.Required())),
        poxxy.Value("email", &user.Email, poxxy.WithValidators(poxxy.Required(), poxxy.Email())),
    )

    if err := schema.ApplyHTTPRequest(r); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Process validated user data
}
ApplyJSON

Validate JSON data directly.

jsonData := `{"name": "John", "email": "john@example.com"}`
if err := schema.ApplyJSON([]byte(jsonData)); err != nil {
    // Handle validation error
}
Supported Content Types
  • application/json - JSON request body
  • application/x-www-form-urlencoded - Form data
  • No content type - Query parameters

Error Handling

Error Types
  • FieldError - Individual field validation error
  • Errors - Collection of field errors
Error Information

Each error includes:

  • Field name
  • Error description
  • Field description (if provided)
if err := schema.Apply(data); err != nil {
    if errors, ok := err.(poxxy.Errors); ok {
        for _, fieldError := range errors {
            fmt.Printf("Field '%s': %v\n", fieldError.Field, fieldError.Error)
        }
    }
}

Advanced Examples

Complex Nested Structure
type User struct {
    Name     string
    Email    *string
    Address  *Address
    Settings map[string]interface{}
    Tags     []string
    Scores   [5]int
}

var user User
schema := poxxy.NewSchema(
    poxxy.Struct("user", &user, poxxy.WithSubSchema(func(s *poxxy.Schema, u *User) {
        poxxy.WithSchema(s, poxxy.Value("name", &u.Name, poxxy.WithValidators(poxxy.Required())))
        poxxy.WithSchema(s, poxxy.Pointer("email", &u.Email, poxxy.WithValidators(poxxy.Email())))
        poxxy.WithSchema(s, poxxy.Pointer("address", &u.Address, poxxy.WithSubSchema(func(ss *poxxy.Schema, addr *Address) {
            poxxy.WithSchema(ss, poxxy.Value("street", &addr.Street, poxxy.WithValidators(poxxy.Required())))
            poxxy.WithSchema(ss, poxxy.Value("city", &addr.City, poxxy.WithValidators(poxxy.Required())))
        })))
        poxxy.WithSchema(s, poxxy.Map("settings", &u.Settings))
        poxxy.WithSchema(s, poxxy.Slice("tags", &u.Tags, poxxy.WithValidators(poxxy.Unique())))
        poxxy.WithSchema(s, poxxy.Array("scores", &u.Scores, poxxy.WithValidators(poxxy.Each(poxxy.Min(0), poxxy.Max(100)))))
    })),
)
Date and Type Conversion
var createdAt time.Time
var updatedAt *time.Time
var unixTimestamp int64

schema := poxxy.NewSchema(
            poxxy.Convert("created_at", &createdAt, func(dateStr string) (*time.Time, error) {
            t, err := time.Parse("2006-01-02", dateStr)
            if err != nil {
                return nil, err
            }
            return &t, nil
        }, poxxy.WithValidators(poxxy.Required())),

    poxxy.ConvertPointer("updated_at", &updatedAt, func(dateStr string) (*time.Time, error) {
        t, err := time.Parse("2006-01-02T15:04:05Z", dateStr)
        if err != nil {
            return nil, err
        }
        return &t, nil
    }),

    poxxy.Convert("timestamp", &unixTimestamp, func(unixTime int64) (*int64, error) {
        return &unixTime, nil
    }, poxxy.WithTransformers(poxxy.CustomTransformer(func(timestamp int64) (int64, error) {
        // Ensure timestamp is not in the future
        if timestamp > time.Now().Unix() {
            return 0, fmt.Errorf("timestamp cannot be in the future")
        }
        return timestamp, nil
    }))),
)

Examples Directory

See the examples/ directory for comprehensive examples:

  • examples/basic/ - Basic usage examples
  • examples/http_basic/ - HTTP request validation
  • examples/http_advanced/ - Advanced HTTP validation
  • examples/advanced_features/ - Default values and transformers
  • examples/date_conversion/ - Date and type conversion examples
  • examples/require/ - Required field validation
  • examples/debug/ - Debugging examples
  • examples/transformers/ - Custom transformers and converters
  • examples/dsl_v2/ - DSL v2 examples

Migration from Transform Fields

The Transform field type has been removed in favor of integrated transformers. Instead of:

// Old way (removed)
poxxy.Transform[string, string]("email", &email, func(email string) (string, error) {
    return strings.ToLower(strings.TrimSpace(email)), nil
})

Use:

// New way
poxxy.Value("email", &email,
    poxxy.WithTransformers(
        poxxy.SanitizeEmail(),
    ),
)

Performance Considerations

  • Two-phase validation: Assignment happens before validation to ensure all fields are populated for cross-field validation
  • Lazy evaluation: Validators are only applied when needed
  • Type safety: Generics ensure compile-time type safety throughout the validation pipeline
  • Memory efficient: Minimal allocations during validation

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass
  6. Submit a pull request

License

MIT License - see LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var MaxBodySize int64 = 5 << 20 // 5MB limit

MaxBodySize is the maximum size of the body of an HTTP request You can change this value to limit the size of the body of an HTTP request

Functions

func WithSchema

func WithSchema(schema *Schema, field Field)

WithSchema adds a field to a schema

Types

type ArrayField

type ArrayField[T any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

ArrayField represents an array field

func (*ArrayField[T]) AddTransformer

func (f *ArrayField[T]) AddTransformer(transformer Transformer[interface{}])

AddTransformer adds a transformer to the field

func (*ArrayField[T]) AppendValidators

func (f *ArrayField[T]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*ArrayField[T]) Assign

func (f *ArrayField[T]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*ArrayField[T]) Description

func (f *ArrayField[T]) Description() string

Description returns the field description

func (*ArrayField[T]) Name

func (f *ArrayField[T]) Name() string

Name returns the field name

func (*ArrayField[T]) SetDefaultValue

func (f *ArrayField[T]) SetDefaultValue(defaultValue interface{})

SetDefaultValue sets the default value for the field

func (*ArrayField[T]) SetDescription

func (f *ArrayField[T]) SetDescription(description string)

SetDescription sets the field description

func (*ArrayField[T]) Validate

func (f *ArrayField[T]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*ArrayField[T]) Value

func (f *ArrayField[T]) Value() interface{}

Value returns the current value of the field

type ContentTypeParsing

type ContentTypeParsing uint8
const (
	ContentTypeParsingAuto ContentTypeParsing = iota
	ContentTypeParsingJSON
	ContentTypeParsingForm
	ContentTypeParsingQuery
)

type ConvertField

type ConvertField[From, To any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

ConvertField represents a field with type conversion

func (*ConvertField[From, To]) AddTransformer

func (f *ConvertField[From, To]) AddTransformer(transformer Transformer[To])

AddTransformer adds a transformer to the field

func (*ConvertField[From, To]) AppendValidators

func (f *ConvertField[From, To]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*ConvertField[From, To]) Assign

func (f *ConvertField[From, To]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*ConvertField[From, To]) Description

func (f *ConvertField[From, To]) Description() string

Description returns the field description

func (*ConvertField[From, To]) Name

func (f *ConvertField[From, To]) Name() string

Name returns the field name

func (*ConvertField[From, To]) SetDefaultValue

func (f *ConvertField[From, To]) SetDefaultValue(defaultValue To)

SetDefaultValue sets the default value for the field

func (*ConvertField[From, To]) SetDescription

func (f *ConvertField[From, To]) SetDescription(description string)

SetDescription sets the field description

func (*ConvertField[From, To]) Validate

func (f *ConvertField[From, To]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*ConvertField[From, To]) Value

func (f *ConvertField[From, To]) Value() interface{}

Value returns the current value of the field

type ConvertPointerField

type ConvertPointerField[From, To any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

ConvertPointerField represents a field with type conversion to pointer

func (*ConvertPointerField[From, To]) AddTransformer

func (f *ConvertPointerField[From, To]) AddTransformer(transformer Transformer[To])

AddTransformer adds a transformer to the field

func (*ConvertPointerField[From, To]) AppendValidators

func (f *ConvertPointerField[From, To]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*ConvertPointerField[From, To]) Assign

func (f *ConvertPointerField[From, To]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*ConvertPointerField[From, To]) Description

func (f *ConvertPointerField[From, To]) Description() string

Description returns the field description

func (*ConvertPointerField[From, To]) Name

func (f *ConvertPointerField[From, To]) Name() string

Name returns the field name

func (*ConvertPointerField[From, To]) SetDefaultValue

func (f *ConvertPointerField[From, To]) SetDefaultValue(defaultValue To)

SetDefaultValue sets the default value for the field

func (*ConvertPointerField[From, To]) SetDescription

func (f *ConvertPointerField[From, To]) SetDescription(description string)

SetDescription sets the field description

func (*ConvertPointerField[From, To]) Validate

func (f *ConvertPointerField[From, To]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*ConvertPointerField[From, To]) Value

func (f *ConvertPointerField[From, To]) Value() interface{}

Value returns the current value of the field

type DefaultOption

type DefaultOption[T any] struct {
	// contains filtered or unexported fields
}

DefaultOption holds a default value

func (DefaultOption[T]) Apply

func (o DefaultOption[T]) Apply(field interface{})

Apply applies the default value to the field

type DefaultValueSetter

type DefaultValueSetter[T any] interface {
	SetDefaultValue(defaultValue T)
}

DefaultValueSetter is an interface for fields that can set default values

type DescriptionOption

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

DescriptionOption holds a description

func (DescriptionOption) Apply

func (o DescriptionOption) Apply(field interface{})

Apply applies the description to the field

type Errors

type Errors []FieldError

Errors represents multiple validation errors

func (Errors) Error

func (e Errors) Error() string

Error returns a string representation of all validation errors

type Field

type Field interface {
	// Name returns the name of the field
	Name() string
	// Value returns the current value of the field
	Value() interface{}
	// Description returns the description of the field
	Description() string
	// SetDescription sets the description of the field
	SetDescription(description string)
	// Assign assigns a value to the field from the input data
	Assign(data map[string]interface{}, schema *Schema) error
	// Validate validates the field value using all registered validators
	Validate(schema *Schema) error
}

Field represents a field definition in a schema

func Array

func Array[T any](name string, ptr interface{}, opts ...Option) Field

Array creates an array field

func Convert

func Convert[From, To any](name string, ptr *To, convert func(From) (*To, error), opts ...Option) Field

Convert creates a conversion field

func ConvertPointer

func ConvertPointer[From, To any](name string, ptr **To, convert func(From) (*To, error), opts ...Option) Field

ConvertPointer creates a conversion field for pointer types

func HTTPMap

func HTTPMap[K comparable, V any](name string, ptr *map[K]V, opts ...Option) Field

HTTPMap creates an HTTP map field

func Map

func Map[K comparable, V any](name string, ptr *map[K]V, opts ...Option) Field

Map creates a map field

func NestedMap

func NestedMap[K comparable, V any](name string, ptr *map[K]V, opts ...Option) Field

NestedMap creates a nested map field

func Pointer

func Pointer[T any](name string, ptr **T, opts ...Option) Field

Pointer creates a pointer field

func Slice

func Slice[T any](name string, ptr *[]T, opts ...Option) Field

Slice creates a slice field.

func Struct

func Struct[T any](name string, ptr *T, opts ...Option) Field

Struct creates a struct field

func Union

func Union(name string, ptr interface{}, resolver func(map[string]interface{}) (interface{}, error)) Field

Union creates a union field

func Value

func Value[T any](name string, ptr *T, opts ...Option) Field

Value creates a value field

func ValueWithoutAssign

func ValueWithoutAssign[T any](name string, opts ...Option) Field

ValueWithoutAssign validates a direct value (used in map validation)

type FieldError

type FieldError struct {
	Field       string
	Description string
	Error       error
}

FieldError represents a validation error for a specific field

type HTTPMapCallbackOption

type HTTPMapCallbackOption[K comparable, V any] struct {
	// contains filtered or unexported fields
}

HTTPMapCallbackOption holds a callback function for HTTPMap

func (HTTPMapCallbackOption[K, V]) Apply

func (o HTTPMapCallbackOption[K, V]) Apply(field interface{})

Apply applies the callback to the HTTPMap field

type HTTPMapField

type HTTPMapField[K comparable, V any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

HTTPMapField represents a map field where each value is a struct

func (*HTTPMapField[K, V]) AppendValidators

func (f *HTTPMapField[K, V]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*HTTPMapField[K, V]) Assign

func (f *HTTPMapField[K, V]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*HTTPMapField[K, V]) Description

func (f *HTTPMapField[K, V]) Description() string

Description returns the field description

func (*HTTPMapField[K, V]) Name

func (f *HTTPMapField[K, V]) Name() string

Name returns the field name

func (*HTTPMapField[K, V]) SetCallback

func (f *HTTPMapField[K, V]) SetCallback(callback func(*Schema, *V))

SetCallback sets the callback function for configuring sub-schemas

func (*HTTPMapField[K, V]) SetDefaultValue

func (f *HTTPMapField[K, V]) SetDefaultValue(defaultValue map[K]V)

SetDefaultValue sets the default value for the field

func (*HTTPMapField[K, V]) SetDescription

func (f *HTTPMapField[K, V]) SetDescription(description string)

SetDescription sets the field description

func (*HTTPMapField[K, V]) Validate

func (f *HTTPMapField[K, V]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*HTTPMapField[K, V]) Value

func (f *HTTPMapField[K, V]) Value() interface{}

Value returns the current value of the field

type HTTPRequestOption

type HTTPRequestOption struct {
	MaxRequestBodySize int64
	ContentTypeParsing ContentTypeParsing
}

type MapField

type MapField[K comparable, V any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

MapField represents a map field

func (*MapField[K, V]) AppendValidators

func (f *MapField[K, V]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*MapField[K, V]) Assign

func (f *MapField[K, V]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*MapField[K, V]) Description

func (f *MapField[K, V]) Description() string

Description returns the field description

func (*MapField[K, V]) Name

func (f *MapField[K, V]) Name() string

Name returns the field name

func (*MapField[K, V]) SetCallback

func (f *MapField[K, V]) SetCallback(callback func(*Schema, K, V))

SetCallback sets the callback function for configuring sub-schemas

func (*MapField[K, V]) SetDefaultValue

func (f *MapField[K, V]) SetDefaultValue(defaultValue map[K]V)

SetDefaultValue sets the default value for the field

func (*MapField[K, V]) SetDescription

func (f *MapField[K, V]) SetDescription(description string)

SetDescription sets the field description

func (*MapField[K, V]) Validate

func (f *MapField[K, V]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*MapField[K, V]) Value

func (f *MapField[K, V]) Value() interface{}

Value returns the current value of the field

type NestedMapField

type NestedMapField[K comparable, V any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

NestedMapField represents a nested map field

func (*NestedMapField[K, V]) AppendValidators

func (f *NestedMapField[K, V]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*NestedMapField[K, V]) Assign

func (f *NestedMapField[K, V]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*NestedMapField[K, V]) Description

func (f *NestedMapField[K, V]) Description() string

Description returns the field description

func (*NestedMapField[K, V]) Name

func (f *NestedMapField[K, V]) Name() string

Name returns the field name

func (*NestedMapField[K, V]) SetCallback

func (f *NestedMapField[K, V]) SetCallback(callback func(*Schema, K, V))

SetCallback implements SubSchemaMapInterface

func (*NestedMapField[K, V]) SetDefaultValue

func (f *NestedMapField[K, V]) SetDefaultValue(defaultValue map[K]V)

SetDefaultValue sets the default value for the field

func (*NestedMapField[K, V]) SetDescription

func (f *NestedMapField[K, V]) SetDescription(description string)

SetDescription sets the field description

func (*NestedMapField[K, V]) Validate

func (f *NestedMapField[K, V]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*NestedMapField[K, V]) Value

func (f *NestedMapField[K, V]) Value() interface{}

Value returns the current value of the field

type Option

type Option interface {
	Apply(interface{})
}

Option represents a configuration option

func WithDefault

func WithDefault[T any](defaultValue T) Option

WithDefault creates a default value option

func WithDescription

func WithDescription(description string) Option

WithDescription creates a description option

func WithHTTPMapCallback

func WithHTTPMapCallback[K comparable, V any](callback func(*Schema, *V)) Option

WithHTTPMapCallback creates a callback option for HTTPMap

func WithSubSchema

func WithSubSchema[T any](callback func(*Schema, *T)) Option

WithSubSchema creates a sub-schema option

func WithSubSchemaMap

func WithSubSchemaMap[K comparable, V any](callback func(*Schema, K, V)) Option

WithSubSchemaMap creates a map sub-schema option

func WithTransformers

func WithTransformers[T any](transformers ...Transformer[T]) Option

WithTransformers creates a transformers option

func WithValidators

func WithValidators(validators ...Validator) Option

WithValidators creates a validators option

type PointerField

type PointerField[T any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

PointerField represents a pointer field

func (*PointerField[T]) AddTransformer

func (f *PointerField[T]) AddTransformer(transformer Transformer[T])

AddTransformer adds a transformer to the field

func (*PointerField[T]) AppendValidators

func (f *PointerField[T]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*PointerField[T]) Assign

func (f *PointerField[T]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*PointerField[T]) Description

func (f *PointerField[T]) Description() string

Description returns the field description

func (*PointerField[T]) Name

func (f *PointerField[T]) Name() string

Name returns the field name

func (*PointerField[T]) SetCallback

func (f *PointerField[T]) SetCallback(callback func(*Schema, *T))

SetCallback sets the callback function for configuring sub-schemas

func (*PointerField[T]) SetDefaultValue

func (f *PointerField[T]) SetDefaultValue(defaultValue T)

SetDefaultValue sets the default value for the field

func (*PointerField[T]) SetDescription

func (f *PointerField[T]) SetDescription(description string)

SetDescription sets the field description

func (*PointerField[T]) Validate

func (f *PointerField[T]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*PointerField[T]) Value

func (f *PointerField[T]) Value() interface{}

Value returns the current value of the field

type RequiredValidator

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

RequiredValidator is a special validator that needs access to the schema

func (RequiredValidator) Validate

func (v RequiredValidator) Validate(value interface{}, fieldName string) error

Validate validates that the field is present and not empty

func (RequiredValidator) ValidateWithSchema

func (v RequiredValidator) ValidateWithSchema(schema *Schema, fieldName string) error

ValidateWithSchema validates field presence using schema context

func (RequiredValidator) WithMessage

func (v RequiredValidator) WithMessage(msg string) Validator

WithMessage sets a custom error message for the validator

type Schema

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

Schema represents a validation schema

func NewSchema

func NewSchema(fields ...Field) *Schema

NewSchema creates a new schema with the given fields

func (*Schema) Apply

func (s *Schema) Apply(data map[string]interface{}, options ...SchemaOption) error

Apply assigns data to variables and validates them

func (*Schema) ApplyHTTPRequest

func (s *Schema) ApplyHTTPRequest(w http.ResponseWriter, r *http.Request, httpRequestOption *HTTPRequestOption, options ...SchemaOption) error

ApplyHTTPRequest assigns data from an HTTP request to a schema It supports application/json and application/x-www-form-urlencoded It will return an error if the content type is not supported

func (*Schema) ApplyJSON

func (s *Schema) ApplyJSON(jsonData []byte, options ...SchemaOption) error

ApplyJSON assigns data from a JSON string to a schema

func (*Schema) GetFieldValue

func (s *Schema) GetFieldValue(fieldName string) (interface{}, bool)

GetFieldValue returns the value of a field by name

func (*Schema) IsFieldPresent

func (s *Schema) IsFieldPresent(fieldName string) bool

IsFieldPresent checks if a field was present in the input data

func (*Schema) SetFieldPresent

func (s *Schema) SetFieldPresent(fieldName string)

SetFieldPresent marks a field as present in the input data

type SchemaOption

type SchemaOption func(*Schema)

SchemaOption represents a configuration option for a schema

func WithSkipValidators

func WithSkipValidators(skipValidators bool) SchemaOption

WithSkipValidators creates a schema option to skip validation

type SliceField

type SliceField[T any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

SliceField represents a slice field where each element is a struct

func (*SliceField[T]) AddTransformer

func (f *SliceField[T]) AddTransformer(transformer Transformer[[]T])

AddTransformer adds a transformer to the field

func (*SliceField[T]) AppendValidators

func (f *SliceField[T]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*SliceField[T]) Assign

func (f *SliceField[T]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*SliceField[T]) Description

func (f *SliceField[T]) Description() string

Description returns the field description

func (*SliceField[T]) Name

func (f *SliceField[T]) Name() string

Name returns the field name

func (*SliceField[T]) SetCallback

func (f *SliceField[T]) SetCallback(callback func(*Schema, *T))

SetCallback sets the callback function for configuring sub-schemas

func (*SliceField[T]) SetDefaultValue

func (f *SliceField[T]) SetDefaultValue(defaultValue []T)

SetDefaultValue sets the default value for the field

func (*SliceField[T]) SetDescription

func (f *SliceField[T]) SetDescription(description string)

SetDescription sets the field description

func (*SliceField[T]) Validate

func (f *SliceField[T]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*SliceField[T]) Value

func (f *SliceField[T]) Value() interface{}

Value returns the current value of the field

type StructField

type StructField[T any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

StructField represents a struct field with callback

func (*StructField[T]) AppendValidators

func (f *StructField[T]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*StructField[T]) Assign

func (f *StructField[T]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*StructField[T]) Description

func (f *StructField[T]) Description() string

Description returns the field description

func (*StructField[T]) Name

func (f *StructField[T]) Name() string

Name returns the field name

func (*StructField[T]) SetCallback

func (f *StructField[T]) SetCallback(callback func(*Schema, *T))

SetCallback sets the callback function for configuring sub-schemas

func (*StructField[T]) SetDefaultValue

func (f *StructField[T]) SetDefaultValue(defaultValue T)

SetDefaultValue sets the default value for the field

func (*StructField[T]) SetDescription

func (f *StructField[T]) SetDescription(description string)

SetDescription sets the field description

func (*StructField[T]) Validate

func (f *StructField[T]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*StructField[T]) Value

func (f *StructField[T]) Value() interface{}

Value returns the current value of the field

type SubSchemaInterface

type SubSchemaInterface[T any] interface {
	SetCallback(func(*Schema, *T))
}

SubSchemaInterface is an interface for fields that can set sub-schema callbacks

type SubSchemaMapInterface

type SubSchemaMapInterface[K comparable, V any] interface {
	SetCallback(func(*Schema, K, V))
}

SubSchemaMapInterface is an interface for map fields that can set sub-schema callbacks

type SubSchemaMapOption

type SubSchemaMapOption[K comparable, V any] struct {
	// contains filtered or unexported fields
}

SubSchemaMapOption holds a callback for configuring map sub-schemas

func (SubSchemaMapOption[K, V]) Apply

func (o SubSchemaMapOption[K, V]) Apply(field interface{})

Apply applies the map sub-schema callback to the field

type SubSchemaOption

type SubSchemaOption[T any] struct {
	// contains filtered or unexported fields
}

SubSchemaOption holds a callback for configuring sub-schemas

func (SubSchemaOption[T]) Apply

func (o SubSchemaOption[T]) Apply(field interface{})

Apply applies the sub-schema callback to the field

type Transformer

type Transformer[T any] interface {
	// Transform applies the transformation to the given value
	Transform(value T) (T, error)
}

Transformer represents a function that transforms a value before assignment and validation

func Capitalize

func Capitalize() Transformer[string]

Capitalize transforms a string to capitalize first letter

func CustomTransformer

func CustomTransformer[T any](transform func(T) (T, error)) Transformer[T]

CustomTransformer creates a custom transformer from a function

func SanitizeEmail

func SanitizeEmail() Transformer[string]

SanitizeEmail normalizes email addresses

func TitleCase

func TitleCase() Transformer[string]

TitleCase transforms a string to title case

func ToLower

func ToLower() Transformer[string]

ToLower transforms a string to lowercase

func ToUpper

func ToUpper() Transformer[string]

ToUpper transforms a string to uppercase

func TrimSpace

func TrimSpace() Transformer[string]

TrimSpace removes leading and trailing whitespace

type TransformerFn

type TransformerFn[T any] struct {
	// contains filtered or unexported fields
}

TransformerFn is a function that implements Transformer

func (TransformerFn[T]) Transform

func (t TransformerFn[T]) Transform(value T) (T, error)

Transform applies the transformation function to the value

type TransformerOption

type TransformerOption[T any] struct {
	// contains filtered or unexported fields
}

TransformerOption holds transformers

func (TransformerOption[T]) Apply

func (o TransformerOption[T]) Apply(field interface{})

Apply applies the transformers to the field

type UnionField

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

UnionField represents a union/polymorphic field

func (*UnionField) Assign

func (f *UnionField) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*UnionField) Description

func (f *UnionField) Description() string

Description returns the field description

func (*UnionField) Name

func (f *UnionField) Name() string

Name returns the field name

func (*UnionField) SetDescription

func (f *UnionField) SetDescription(description string)

SetDescription sets the field description

func (*UnionField) Validate

func (f *UnionField) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*UnionField) Value

func (f *UnionField) Value() interface{}

Value returns the current value of the field

type Validator

type Validator interface {
	// Validate validates a value and returns an error if validation fails
	Validate(value interface{}, fieldName string) error
	// WithMessage sets a custom error message for the validator
	WithMessage(msg string) Validator
}

Validator represents a validation function

func Each

func Each(validators ...Validator) Validator

Each validator applies validators to each element of a slice/array

func Email

func Email() Validator

Email validator validates email format

func In

func In(values ...interface{}) Validator

In validator validates that a value is one of the specified values

func Max

func Max(max interface{}) Validator

Max validator validates that a numeric value is at most the specified maximum

func MaxLength

func MaxLength(maxLen int) Validator

MaxLength validator validates that a string or slice has at most the specified length

func Min

func Min(min interface{}) Validator

Min validator validates that a numeric value is at least the specified minimum

func MinLength

func MinLength(minLen int) Validator

MinLength validator validates that a string or slice has at least the specified length

func NewInterfaceValidator

func NewInterfaceValidator(fn func(interface{}, string) error) Validator

NewInterfaceValidator creates a validator that can handle interface{} values This is used for backward compatibility with existing validators

func NotEmpty

func NotEmpty() Validator

NotEmpty validator - rejects zero values (use this for non-zero value requirements)

func Required

func Required() Validator

Required validator - checks if field was present in input data, not if value is non-zero

func URL

func URL() Validator

URL validator validates URL format

func Unique

func Unique() Validator

Unique validator ensures all elements in slices, arrays, or maps are unique

func UniqueBy

func UniqueBy(keyExtractor func(interface{}) interface{}) Validator

UniqueBy validator ensures all elements in slices/arrays are unique by a specific key extractor function

func ValidatorFunc

func ValidatorFunc[T any](fn func(value T, fieldName string) error) Validator

ValidatorFunc creates a custom validator from a function (simplified version)

func WithMapKeys

func WithMapKeys(keys ...string) Validator

WithMapKeys validator ensures that a map contains all the specified keys

type ValidatorFn

type ValidatorFn[T any] struct {
	// contains filtered or unexported fields
}

ValidatorFn is a generic function that implements Validator

func NewValidatorFn

func NewValidatorFn[T any](fn func(T, string) error) ValidatorFn[T]

NewValidatorFn creates a new ValidatorFn with type safety

func (ValidatorFn[T]) Validate

func (v ValidatorFn[T]) Validate(value interface{}, fieldName string) error

Validate validates a value using the validator function

func (ValidatorFn[T]) WithMessage

func (v ValidatorFn[T]) WithMessage(msg string) Validator

WithMessage sets a custom error message for the validator

type ValidatorsAppender

type ValidatorsAppender interface {
	AppendValidators(validators []Validator)
}

ValidatorsAppender is an interface for fields that can append validators

type ValidatorsOption

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

ValidatorsOption holds validators

func (ValidatorsOption) Apply

func (o ValidatorsOption) Apply(field interface{})

Apply applies the validators to the field

type ValueField

type ValueField[T any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

ValueField represents a basic value field

func (*ValueField[T]) AddTransformer

func (f *ValueField[T]) AddTransformer(transformer Transformer[T])

AddTransformer adds a transformer to the field

func (*ValueField[T]) AppendValidators

func (f *ValueField[T]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*ValueField[T]) Assign

func (f *ValueField[T]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*ValueField[T]) Description

func (f *ValueField[T]) Description() string

Description returns the field description

func (*ValueField[T]) Name

func (f *ValueField[T]) Name() string

Name returns the field name

func (*ValueField[T]) SetDefaultValue

func (f *ValueField[T]) SetDefaultValue(defaultValue T)

SetDefaultValue sets the default value for the field

func (*ValueField[T]) SetDescription

func (f *ValueField[T]) SetDescription(description string)

SetDescription sets the field description

func (*ValueField[T]) Validate

func (f *ValueField[T]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*ValueField[T]) Value

func (f *ValueField[T]) Value() interface{}

Value returns the current value of the field

type ValueWithoutAssignField

type ValueWithoutAssignField[T any] struct {
	Validators []Validator
	// contains filtered or unexported fields
}

ValueWithoutAssignField represents a field that validates a direct value

func (*ValueWithoutAssignField[T]) AppendValidators

func (f *ValueWithoutAssignField[T]) AppendValidators(validators []Validator)

AppendValidators implements ValidatorsAppender interface

func (*ValueWithoutAssignField[T]) Assign

func (f *ValueWithoutAssignField[T]) Assign(data map[string]interface{}, schema *Schema) error

Assign assigns a value to the field from the input data

func (*ValueWithoutAssignField[T]) Description

func (f *ValueWithoutAssignField[T]) Description() string

Description returns the field description

func (*ValueWithoutAssignField[T]) Name

func (f *ValueWithoutAssignField[T]) Name() string

Name returns the field name

func (*ValueWithoutAssignField[T]) SetDescription

func (f *ValueWithoutAssignField[T]) SetDescription(description string)

SetDescription sets the field description

func (*ValueWithoutAssignField[T]) Validate

func (f *ValueWithoutAssignField[T]) Validate(schema *Schema) error

Validate validates the field value using all registered validators

func (*ValueWithoutAssignField[T]) Value

func (f *ValueWithoutAssignField[T]) Value() interface{}

Value returns the current value of the field

Directories

Path Synopsis
basic command
date_conversion command
debug command
dsl_v2 command
http_advanced command
http_basic command
require command
transformers command

Jump to

Keyboard shortcuts

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