πΉ Validation in TuskLang - Go Guide
Validation in TuskLang - Go Guide
π― The Power of Configuration Validation
In TuskLang, validation isn't just about checking valuesβit's about ensuring your configuration is bulletproof. We don't bow to any king, especially not invalid or unsafe configurations. TuskLang gives you the power to validate your configuration at multiple levels with comprehensive, type-safe validation rules.
π Table of Contents
- Understanding Validation in TuskLang - Basic Validation Syntax - Schema Validation - Custom Validation Rules - Go Integration - Validation Patterns - Performance Considerations - Real-World Examples - Best Practicesπ Understanding Validation in TuskLang
TuskLang provides comprehensive validation at configuration time:
// TuskLang - Validation configuration
[validation_config]
required_fields: @validate.required(["api_key", "database_url"])
email_validation: @validate.email(@env("ADMIN_EMAIL"))
url_validation: @validate.url(@env("API_BASE_URL"))
numeric_validation: @validate.numeric(@env("PORT"))
range_validation: @validate.range(@env("TIMEOUT"), 1, 300)
// Go integration
type ValidationConfig struct {
RequiredFields []string tsk:"required_fields"
EmailValidation bool tsk:"email_validation"
URLValidation bool tsk:"url_validation"
NumericValidation bool tsk:"numeric_validation"
RangeValidation bool tsk:"range_validation"
}
π¨ Basic Validation Syntax
Required Field Validation
// TuskLang - Required field validation
[required_validation]
api_key: @validate.required(["API_KEY", "DATABASE_URL"])
user_fields: @validate.required(["USER_ID", "USER_NAME", "USER_EMAIL"])
config_fields: @validate.required(["APP_NAME", "ENVIRONMENT"])
// Go - Required field validation handling
type RequiredValidation struct {
APIKey []string tsk:"api_key"
UserFields []string tsk:"user_fields"
ConfigFields []string tsk:"config_fields"
}func (r *RequiredValidation) ValidateRequired(fields []string) error {
for _, field := range fields {
if os.Getenv(field) == "" {
return fmt.Errorf("required field %s is missing", field)
}
}
return nil
}
func (r *RequiredValidation) ValidateAll() error {
if err := r.ValidateRequired(r.APIKey); err != nil {
return fmt.Errorf("API validation failed: %w", err)
}
if err := r.ValidateRequired(r.UserFields); err != nil {
return fmt.Errorf("user validation failed: %w", err)
}
if err := r.ValidateRequired(r.ConfigFields); err != nil {
return fmt.Errorf("config validation failed: %w", err)
}
return nil
}
Type Validation
// TuskLang - Type validation
[type_validation]
string_fields: @validate.string(["APP_NAME", "ENVIRONMENT"])
numeric_fields: @validate.numeric(["PORT", "TIMEOUT", "MAX_CONNECTIONS"])
boolean_fields: @validate.boolean(["DEBUG", "ENABLE_CACHE"])
// Go - Type validation handling
type TypeValidation struct {
StringFields []string tsk:"string_fields"
NumericFields []string tsk:"numeric_fields"
BooleanFields []string tsk:"boolean_fields"
}func (t *TypeValidation) ValidateString(fields []string) error {
for _, field := range fields {
value := os.Getenv(field)
if value == "" {
return fmt.Errorf("string field %s is empty", field)
}
}
return nil
}
func (t *TypeValidation) ValidateNumeric(fields []string) error {
for _, field := range fields {
value := os.Getenv(field)
if value == "" {
return fmt.Errorf("numeric field %s is empty", field)
}
if _, err := strconv.Atoi(value); err != nil {
return fmt.Errorf("field %s must be numeric, got: %s", field, value)
}
}
return nil
}
func (t *TypeValidation) ValidateBoolean(fields []string) error {
for _, field := range fields {
value := os.Getenv(field)
if value == "" {
return fmt.Errorf("boolean field %s is empty", field)
}
if value != "true" && value != "false" {
return fmt.Errorf("field %s must be boolean (true/false), got: %s", field, value)
}
}
return nil
}
Format Validation
// TuskLang - Format validation
[format_validation]
email_validation: @validate.email(@env("ADMIN_EMAIL"))
url_validation: @validate.url(@env("API_BASE_URL"))
ip_validation: @validate.ip(@env("SERVER_IP"))
uuid_validation: @validate.uuid(@env("SESSION_ID"))
// Go - Format validation handling
type FormatValidation struct {
EmailValidation bool tsk:"email_validation"
URLValidation bool tsk:"url_validation"
IPValidation bool tsk:"ip_validation"
UUIDValidation bool tsk:"uuid_validation"
}func (f *FormatValidation) ValidateEmail(email string) bool {
emailRegex := regexp.MustCompile(^[^\s@]+@[^\s@]+\.[^\s@]+$
)
return emailRegex.MatchString(email)
}
func (f *FormatValidation) ValidateURL(url string) bool {
_, err := url.Parse(url)
return err == nil
}
func (f *FormatValidation) ValidateIP(ip string) bool {
return net.ParseIP(ip) != nil
}
func (f *FormatValidation) ValidateUUID(uuid string) bool {
uuidRegex := regexp.MustCompile(^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
)
return uuidRegex.MatchString(strings.ToLower(uuid))
}
π Schema Validation
JSON Schema Validation
// TuskLang - JSON schema validation
[schema_validation]
user_schema: @validate.schema({
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 18, "maximum": 100}
},
"required": ["name", "email"]
})config_schema: @validate.schema({
"type": "object",
"properties": {
"app_name": {"type": "string"},
"port": {"type": "integer", "minimum": 1, "maximum": 65535},
"debug": {"type": "boolean"}
},
"required": ["app_name", "port"]
})
// Go - JSON schema validation handling
type SchemaValidation struct {
UserSchema string tsk:"user_schema"
ConfigSchema string tsk:"config_schema"
}type User struct {
Name string json:"name"
Email string json:"email"
Age int json:"age,omitempty"
}
type Config struct {
AppName string json:"app_name"
Port int json:"port"
Debug bool json:"debug,omitempty"
}
func (s *SchemaValidation) ValidateUser(user User) error {
// Validate required fields
if user.Name == "" {
return errors.New("name is required")
}
if user.Email == "" {
return errors.New("email is required")
}
// Validate email format
emailRegex := regexp.MustCompile(^[^\s@]+@[^\s@]+\.[^\s@]+$
)
if !emailRegex.MatchString(user.Email) {
return errors.New("invalid email format")
}
// Validate age range
if user.Age > 0 && (user.Age < 18 || user.Age > 100) {
return errors.New("age must be between 18 and 100")
}
return nil
}
func (s *SchemaValidation) ValidateConfig(config Config) error {
// Validate required fields
if config.AppName == "" {
return errors.New("app_name is required")
}
if config.Port <= 0 {
return errors.New("port is required")
}
// Validate port range
if config.Port < 1 || config.Port > 65535 {
return errors.New("port must be between 1 and 65535")
}
return nil
}
Custom Schema Validation
// TuskLang - Custom schema validation
[custom_schema]
database_schema: @validate.custom_schema({
"host": {"type": "string", "required": true},
"port": {"type": "integer", "min": 1, "max": 65535},
"name": {"type": "string", "required": true},
"ssl": {"type": "boolean", "default": false}
})api_schema: @validate.custom_schema({
"base_url": {"type": "string", "format": "url", "required": true},
"timeout": {"type": "integer", "min": 1, "max": 300},
"retries": {"type": "integer", "min": 0, "max": 10}
})
// Go - Custom schema validation handling
type CustomSchema struct {
DatabaseSchema string tsk:"database_schema"
APISchema string tsk:"api_schema"
}type DatabaseConfig struct {
Host string json:"host"
Port int json:"port"
Name string json:"name"
SSL bool json:"ssl"
}
type APIConfig struct {
BaseURL string json:"base_url"
Timeout int json:"timeout"
Retries int json:"retries"
}
func (c *CustomSchema) ValidateDatabase(config DatabaseConfig) error {
if config.Host == "" {
return errors.New("database host is required")
}
if config.Port < 1 || config.Port > 65535 {
return errors.New("database port must be between 1 and 65535")
}
if config.Name == "" {
return errors.New("database name is required")
}
return nil
}
func (c *CustomSchema) ValidateAPI(config APIConfig) error {
if config.BaseURL == "" {
return errors.New("API base URL is required")
}
if _, err := url.Parse(config.BaseURL); err != nil {
return fmt.Errorf("invalid API base URL: %w", err)
}
if config.Timeout < 1 || config.Timeout > 300 {
return errors.New("API timeout must be between 1 and 300 seconds")
}
if config.Retries < 0 || config.Retries > 10 {
return errors.New("API retries must be between 0 and 10")
}
return nil
}
π§ Custom Validation Rules
Business Logic Validation
// TuskLang - Business logic validation
[business_validation]
password_strength: @validate.custom("password", "min_length:8,uppercase:true,number:true")
age_validation: @validate.range(@env("AGE"), 18, 100)
file_size: @validate.max_size("upload.txt", "10MB")
domain_validation: @validate.custom("domain", "allowed_domains:example.com,test.com")
// Go - Business logic validation handling
type BusinessValidation struct {
PasswordStrength bool tsk:"password_strength"
AgeValidation bool tsk:"age_validation"
FileSize bool tsk:"file_size"
DomainValidation bool tsk:"domain_validation"
}func (b *BusinessValidation) ValidatePassword(password string) error {
if len(password) < 8 {
return errors.New("password must be at least 8 characters long")
}
hasUpper := regexp.MustCompile([A-Z]
).MatchString(password)
if !hasUpper {
return errors.New("password must contain at least one uppercase letter")
}
hasNumber := regexp.MustCompile([0-9]
).MatchString(password)
if !hasNumber {
return errors.New("password must contain at least one number")
}
return nil
}
func (b *BusinessValidation) ValidateAge(age int) error {
if age < 18 || age > 100 {
return errors.New("age must be between 18 and 100")
}
return nil
}
func (b *BusinessValidation) ValidateFileSize(path string, maxSize string) error {
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("failed to get file info: %w", err)
}
size := info.Size()
maxBytes := parseSize(maxSize)
if size > maxBytes {
return fmt.Errorf("file size %d bytes exceeds maximum %d bytes", size, maxBytes)
}
return nil
}
func (b *BusinessValidation) ValidateDomain(domain string, allowedDomains []string) error {
for _, allowed := range allowedDomains {
if domain == allowed {
return nil
}
}
return fmt.Errorf("domain %s is not in allowed list: %v", domain, allowedDomains)
}
func parseSize(sizeStr string) int64 {
if strings.HasSuffix(sizeStr, "MB") {
size, _ := strconv.Atoi(strings.TrimSuffix(sizeStr, "MB"))
return int64(size 1024 1024)
}
if strings.HasSuffix(sizeStr, "KB") {
size, _ := strconv.Atoi(strings.TrimSuffix(sizeStr, "KB"))
return int64(size * 1024)
}
size, _ := strconv.Atoi(sizeStr)
return int64(size)
}
Cross-Field Validation
// TuskLang - Cross-field validation
[cross_field_validation]
password_match: @validate.match(@env("PASSWORD"), @env("PASSWORD_CONFIRM"))
date_range: @validate.date_range(@env("START_DATE"), @env("END_DATE"))
numeric_range: @validate.numeric_range(@env("MIN_VALUE"), @env("MAX_VALUE"))
// Go - Cross-field validation handling
type CrossFieldValidation struct {
PasswordMatch bool tsk:"password_match"
DateRange bool tsk:"date_range"
NumericRange bool tsk:"numeric_range"
}func (c *CrossFieldValidation) ValidatePasswordMatch(password, confirm string) error {
if password != confirm {
return errors.New("passwords do not match")
}
return nil
}
func (c *CrossFieldValidation) ValidateDateRange(startDate, endDate string) error {
start, err := time.Parse("2006-01-02", startDate)
if err != nil {
return fmt.Errorf("invalid start date: %w", err)
}
end, err := time.Parse("2006-01-02", endDate)
if err != nil {
return fmt.Errorf("invalid end date: %w", err)
}
if start.After(end) {
return errors.New("start date must be before end date")
}
return nil
}
func (c *CrossFieldValidation) ValidateNumericRange(min, max string) error {
minVal, err := strconv.Atoi(min)
if err != nil {
return fmt.Errorf("invalid minimum value: %w", err)
}
maxVal, err := strconv.Atoi(max)
if err != nil {
return fmt.Errorf("invalid maximum value: %w", err)
}
if minVal >= maxVal {
return errors.New("minimum value must be less than maximum value")
}
return nil
}
π§ Go Integration
Validation Engine
// Go - Validation engine
type ValidationEngine struct {
validators map[string]ValidatorFunc
}type ValidatorFunc func(value interface{}) error
func NewValidationEngine() *ValidationEngine {
return &ValidationEngine{
validators: make(map[string]ValidatorFunc),
}
}
func (v *ValidationEngine) RegisterValidator(name string, fn ValidatorFunc) {
v.validators[name] = fn
}
func (v *ValidationEngine) Validate(name string, value interface{}) error {
fn, exists := v.validators[name]
if !exists {
return fmt.Errorf("validator '%s' not found", name)
}
return fn(value)
}
// Register common validators
func (v *ValidationEngine) RegisterCommon() {
v.RegisterValidator("required", v.requiredValidator)
v.RegisterValidator("email", v.emailValidator)
v.RegisterValidator("url", v.urlValidator)
v.RegisterValidator("numeric", v.numericValidator)
v.RegisterValidator("range", v.rangeValidator)
}
func (v *ValidationEngine) requiredValidator(value interface{}) error {
if value == nil {
return errors.New("value is required")
}
if str, ok := value.(string); ok && str == "" {
return errors.New("value is required")
}
return nil
}
func (v *ValidationEngine) emailValidator(value interface{}) error {
str, ok := value.(string)
if !ok {
return errors.New("value must be a string")
}
emailRegex := regexp.MustCompile(^[^\s@]+@[^\s@]+\.[^\s@]+$
)
if !emailRegex.MatchString(str) {
return errors.New("invalid email format")
}
return nil
}
func (v *ValidationEngine) urlValidator(value interface{}) error {
str, ok := value.(string)
if !ok {
return errors.New("value must be a string")
}
_, err := url.Parse(str)
if err != nil {
return fmt.Errorf("invalid URL format: %w", err)
}
return nil
}
func (v *ValidationEngine) numericValidator(value interface{}) error {
switch v := value.(type) {
case int, int64, float64:
return nil
case string:
if _, err := strconv.Atoi(v); err != nil {
return errors.New("value must be numeric")
}
return nil
default:
return errors.New("value must be numeric")
}
}
func (v *ValidationEngine) rangeValidator(value interface{}) error {
// This is a simplified implementation
// In practice, you'd want to pass min/max as additional parameters
return nil
}
Validation Chain
// Go - Validation chain
type ValidationChain struct {
engine *ValidationEngine
rules []ValidationRule
}type ValidationRule struct {
Name string
Value interface{}
Args []interface{}
}
func NewValidationChain(engine ValidationEngine) ValidationChain {
return &ValidationChain{
engine: engine,
rules: make([]ValidationRule, 0),
}
}
func (v ValidationChain) AddRule(name string, value interface{}, args ...interface{}) ValidationChain {
v.rules = append(v.rules, ValidationRule{
Name: name,
Value: value,
Args: args,
})
return v
}
func (v *ValidationChain) Validate() error {
for _, rule := range v.rules {
if err := v.engine.Validate(rule.Name, rule.Value); err != nil {
return fmt.Errorf("validation failed for rule '%s': %w", rule.Name, err)
}
}
return nil
}
// Fluent interface example
func (v ValidationChain) Required(value interface{}) ValidationChain {
return v.AddRule("required", value)
}
func (v ValidationChain) Email(value interface{}) ValidationChain {
return v.AddRule("email", value)
}
func (v ValidationChain) URL(value interface{}) ValidationChain {
return v.AddRule("url", value)
}
func (v ValidationChain) Numeric(value interface{}) ValidationChain {
return v.AddRule("numeric", value)
}
π Validation Patterns
Configuration Validation
// TuskLang - Configuration validation
[config_validation]
app_config: @validate.config({
"required": ["APP_NAME", "ENVIRONMENT", "PORT"],
"numeric": ["PORT", "TIMEOUT"],
"boolean": ["DEBUG", "ENABLE_CACHE"],
"email": ["ADMIN_EMAIL"],
"url": ["API_BASE_URL"]
})database_config: @validate.config({
"required": ["DB_HOST", "DB_NAME", "DB_USER"],
"numeric": ["DB_PORT"],
"boolean": ["DB_SSL"]
})
// Go - Configuration validation handling
type ConfigValidation struct {
AppConfig string tsk:"app_config"
DatabaseConfig string tsk:"database_config"
}type AppConfig struct {
AppName string json:"app_name"
Environment string json:"environment"
Port int json:"port"
Timeout int json:"timeout"
Debug bool json:"debug"
EnableCache bool json:"enable_cache"
AdminEmail string json:"admin_email"
APIBaseURL string json:"api_base_url"
}
type DatabaseConfig struct {
Host string json:"host"
Port int json:"port"
Name string json:"name"
User string json:"user"
SSL bool json:"ssl"
}
func (c *ConfigValidation) ValidateAppConfig(config AppConfig) error {
engine := NewValidationEngine()
engine.RegisterCommon()
chain := NewValidationChain(engine)
chain.Required(config.AppName).
Required(config.Environment).
Numeric(config.Port).
Numeric(config.Timeout)
if config.AdminEmail != "" {
chain.Email(config.AdminEmail)
}
if config.APIBaseURL != "" {
chain.URL(config.APIBaseURL)
}
return chain.Validate()
}
func (c *ConfigValidation) ValidateDatabaseConfig(config DatabaseConfig) error {
engine := NewValidationEngine()
engine.RegisterCommon()
chain := NewValidationChain(engine)
chain.Required(config.Host).
Required(config.Name).
Required(config.User).
Numeric(config.Port)
return chain.Validate()
}
Data Validation
// TuskLang - Data validation
[data_validation]
user_data: @validate.data({
"name": {"type": "string", "required": true, "min_length": 1},
"email": {"type": "string", "required": true, "format": "email"},
"age": {"type": "integer", "min": 18, "max": 100}
})order_data: @validate.data({
"items": {"type": "array", "min_items": 1},
"total": {"type": "number", "min": 0},
"customer_id": {"type": "string", "required": true}
})
// Go - Data validation handling
type DataValidation struct {
UserData string tsk:"user_data"
OrderData string tsk:"order_data"
}type User struct {
Name string json:"name"
Email string json:"email"
Age int json:"age"
}
type Order struct {
Items []string json:"items"
Total float64 json:"total"
CustomerID string json:"customer_id"
}
func (d *DataValidation) ValidateUser(user User) error {
if user.Name == "" {
return errors.New("name is required")
}
if len(user.Name) < 1 {
return errors.New("name must be at least 1 character")
}
if user.Email == "" {
return errors.New("email is required")
}
emailRegex := regexp.MustCompile(^[^\s@]+@[^\s@]+\.[^\s@]+$
)
if !emailRegex.MatchString(user.Email) {
return errors.New("invalid email format")
}
if user.Age < 18 || user.Age > 100 {
return errors.New("age must be between 18 and 100")
}
return nil
}
func (d *DataValidation) ValidateOrder(order Order) error {
if len(order.Items) < 1 {
return errors.New("order must have at least one item")
}
if order.Total < 0 {
return errors.New("order total cannot be negative")
}
if order.CustomerID == "" {
return errors.New("customer ID is required")
}
return nil
}
β‘ Performance Considerations
Validation Caching
// Go - Validation caching system
type ValidationCache struct {
cache map[string]bool
mutex sync.RWMutex
ttl time.Duration
}func NewValidationCache(ttl time.Duration) *ValidationCache {
return &ValidationCache{
cache: make(map[string]bool),
ttl: ttl,
}
}
func (v *ValidationCache) Get(key string) (bool, bool) {
v.mutex.RLock()
defer v.mutex.RUnlock()
value, exists := v.cache[key]
return value, exists
}
func (v *ValidationCache) Set(key string, value bool) {
v.mutex.Lock()
defer v.mutex.Unlock()
v.cache[key] = value
// Schedule cleanup
go func() {
time.Sleep(v.ttl)
v.mutex.Lock()
delete(v.cache, key)
v.mutex.Unlock()
}()
}
func (v *ValidationCache) Clear() {
v.mutex.Lock()
defer v.mutex.Unlock()
v.cache = make(map[string]bool)
}
Lazy Validation
// Go - Lazy validation system
type LazyValidator struct {
cache *ValidationCache
engine *ValidationEngine
}func NewLazyValidator(ttl time.Duration) *LazyValidator {
engine := NewValidationEngine()
engine.RegisterCommon()
return &LazyValidator{
cache: NewValidationCache(ttl),
engine: engine,
}
}
func (l *LazyValidator) Validate(name string, value interface{}) error {
// Generate cache key
cacheKey := l.generateCacheKey(name, value)
// Check cache first
if valid, exists := l.cache.Get(cacheKey); exists {
if !valid {
return errors.New("validation failed")
}
return nil
}
// Perform validation
err := l.engine.Validate(name, value)
isValid := err == nil
// Cache the result
l.cache.Set(cacheKey, isValid)
return err
}
func (l *LazyValidator) generateCacheKey(name string, value interface{}) string {
jsonData, _ := json.Marshal(value)
return fmt.Sprintf("%s:%s", name, string(jsonData))
}
π Real-World Examples
API Configuration Validation
// TuskLang - API configuration validation
[api_validation]
api_config: @validate.api_config({
"base_url": {"required": true, "format": "url"},
"timeout": {"required": true, "type": "integer", "min": 1, "max": 300},
"retries": {"type": "integer", "min": 0, "max": 10},
"rate_limit": {"type": "integer", "min": 1},
"auth_token": {"required": true, "min_length": 32}
})endpoint_config: @validate.endpoint_config({
"path": {"required": true, "pattern": "^/[a-zA-Z0-9/_-]+$"},
"method": {"required": true, "enum": ["GET", "POST", "PUT", "DELETE"]},
"timeout": {"type": "integer", "min": 1, "max": 60}
})
// Go - API configuration validation handling
type APIValidation struct {
APIConfig string tsk:"api_config"
EndpointConfig string tsk:"endpoint_config"
}type APIConfig struct {
BaseURL string json:"base_url"
Timeout int json:"timeout"
Retries int json:"retries"
RateLimit int json:"rate_limit"
AuthToken string json:"auth_token"
}
type EndpointConfig struct {
Path string json:"path"
Method string json:"method"
Timeout int json:"timeout"
}
func (a *APIValidation) ValidateAPIConfig(config APIConfig) error {
// Validate base URL
if config.BaseURL == "" {
return errors.New("base URL is required")
}
if _, err := url.Parse(config.BaseURL); err != nil {
return fmt.Errorf("invalid base URL: %w", err)
}
// Validate timeout
if config.Timeout < 1 || config.Timeout > 300 {
return errors.New("timeout must be between 1 and 300 seconds")
}
// Validate retries
if config.Retries < 0 || config.Retries > 10 {
return errors.New("retries must be between 0 and 10")
}
// Validate rate limit
if config.RateLimit < 1 {
return errors.New("rate limit must be at least 1")
}
// Validate auth token
if config.AuthToken == "" {
return errors.New("auth token is required")
}
if len(config.AuthToken) < 32 {
return errors.New("auth token must be at least 32 characters")
}
return nil
}
func (a *APIValidation) ValidateEndpointConfig(config EndpointConfig) error {
// Validate path
if config.Path == "" {
return errors.New("path is required")
}
pathRegex := regexp.MustCompile(^/[a-zA-Z0-9/_-]+$
)
if !pathRegex.MatchString(config.Path) {
return errors.New("invalid path format")
}
// Validate method
validMethods := []string{"GET", "POST", "PUT", "DELETE"}
methodValid := false
for _, method := range validMethods {
if config.Method == method {
methodValid = true
break
}
}
if !methodValid {
return errors.New("invalid HTTP method")
}
// Validate timeout
if config.Timeout < 1 || config.Timeout > 60 {
return errors.New("timeout must be between 1 and 60 seconds")
}
return nil
}
Database Configuration Validation
// TuskLang - Database configuration validation
[database_validation]
postgres_config: @validate.database_config({
"host": {"required": true, "type": "string"},
"port": {"required": true, "type": "integer", "min": 1, "max": 65535},
"name": {"required": true, "type": "string"},
"user": {"required": true, "type": "string"},
"password": {"type": "string"},
"ssl_mode": {"type": "string", "enum": ["disable", "require", "verify-ca", "verify-full"]},
"pool_size": {"type": "integer", "min": 1, "max": 100}
})redis_config: @validate.database_config({
"host": {"required": true, "type": "string"},
"port": {"type": "integer", "min": 1, "max": 65535, "default": 6379},
"password": {"type": "string"},
"db": {"type": "integer", "min": 0, "max": 15, "default": 0},
"timeout": {"type": "integer", "min": 1, "max": 300, "default": 30}
})
// Go - Database configuration validation handling
type DatabaseValidation struct {
PostgresConfig string tsk:"postgres_config"
RedisConfig string tsk:"redis_config"
}type PostgresConfig struct {
Host string json:"host"
Port int json:"port"
Name string json:"name"
User string json:"user"
Password string json:"password"
SSLMode string json:"ssl_mode"
PoolSize int json:"pool_size"
}
type RedisConfig struct {
Host string json:"host"
Port int json:"port"
Password string json:"password"
DB int json:"db"
Timeout int json:"timeout"
}
func (d *DatabaseValidation) ValidatePostgresConfig(config PostgresConfig) error {
// Validate host
if config.Host == "" {
return errors.New("host is required")
}
// Validate port
if config.Port < 1 || config.Port > 65535 {
return errors.New("port must be between 1 and 65535")
}
// Validate database name
if config.Name == "" {
return errors.New("database name is required")
}
// Validate user
if config.User == "" {
return errors.New("user is required")
}
// Validate SSL mode
validSSLModes := []string{"disable", "require", "verify-ca", "verify-full"}
sslModeValid := false
for _, mode := range validSSLModes {
if config.SSLMode == mode {
sslModeValid = true
break
}
}
if config.SSLMode != "" && !sslModeValid {
return errors.New("invalid SSL mode")
}
// Validate pool size
if config.PoolSize < 1 || config.PoolSize > 100 {
return errors.New("pool size must be between 1 and 100")
}
return nil
}
func (d *DatabaseValidation) ValidateRedisConfig(config RedisConfig) error {
// Validate host
if config.Host == "" {
return errors.New("host is required")
}
// Set defaults
if config.Port == 0 {
config.Port = 6379
}
if config.DB == 0 {
config.DB = 0
}
if config.Timeout == 0 {
config.Timeout = 30
}
// Validate port
if config.Port < 1 || config.Port > 65535 {
return errors.New("port must be between 1 and 65535")
}
// Validate database number
if config.DB < 0 || config.DB > 15 {
return errors.New("database number must be between 0 and 15")
}
// Validate timeout
if config.Timeout < 1 || config.Timeout > 300 {
return errors.New("timeout must be between 1 and 300 seconds")
}
return nil
}
π― Best Practices
1. Validate Early and Often
// β
Good - Early validation
func (c *Config) LoadAndValidate() error {
// Load configuration
if err := c.Load(); err != nil {
return fmt.Errorf("failed to load config: %w", err)
}
// Validate immediately
if err := c.Validate(); err != nil {
return fmt.Errorf("configuration validation failed: %w", err)
}
return nil
}// β Bad - Late validation
func (c *Config) Load() error {
// Load configuration without validation
// Validation happens much later, potentially causing issues
return nil
}
2. Use Specific Error Messages
// β
Good - Specific error messages
func (v *Validator) ValidateEmail(email string) error {
if email == "" {
return errors.New("email address is required")
}
emailRegex := regexp.MustCompile(^[^\s@]+@[^\s@]+\.[^\s@]+$
)
if !emailRegex.MatchString(email) {
return fmt.Errorf("invalid email format: %s", email)
}
return nil
}// β Bad - Generic error messages
func (v *Validator) ValidateEmail(email string) error {
if email == "" || !strings.Contains(email, "@") {
return errors.New("validation failed")
}
return nil
}
3. Cache Validation Results
// β
Good - Cached validation
func (l *LazyValidator) Validate(name string, value interface{}) error {
cacheKey := l.generateCacheKey(name, value)
if valid, exists := l.cache.Get(cacheKey); exists {
if !valid {
return errors.New("validation failed")
}
return nil
}
err := l.engine.Validate(name, value)
l.cache.Set(cacheKey, err == nil)
return err
}// β Bad - No caching
func (v *Validator) Validate(name string, value interface{}) error {
// Always perform validation, even for same values
return v.performValidation(name, value)
}
4. Use Type-Safe Validation
// β
Good - Type-safe validation
type UserValidator struct {
user User
}func (v *UserValidator) Validate() error {
if v.user.Name == "" {
return errors.New("name is required")
}
if v.user.Age < 18 {
return errors.New("age must be at least 18")
}
return nil
}
// β Bad - Untyped validation
func ValidateUser(data map[string]interface{}) error {
name, ok := data["name"].(string)
if !ok || name == "" {
return errors.New("name is required")
}
age, ok := data["age"].(int)
if !ok || age < 18 {
return errors.New("age must be at least 18")
}
return nil
}
5. Document Validation Rules
// β
Good - Documented validation rules
type ValidationRules struct {
// User validation rules
// - Name: required, min length 1
// - Email: required, valid email format
// - Age: optional, between 18-100
UserRules map[string]interface{} json:"user_rules"
// Configuration validation rules
// - AppName: required, string
// - Port: required, integer 1-65535
// - Debug: optional, boolean
ConfigRules map[string]interface{} json:"config_rules"
}// β Bad - Undocumented validation rules
type ValidationRules struct {
UserRules map[string]interface{} json:"user_rules"
ConfigRules map[string]interface{} json:"config_rules"
}
---
π You've mastered validation in TuskLang with Go!
Validation in TuskLang ensures your configuration is bulletproof and reliable. With proper validation handling, you can build robust systems that catch errors early and maintain data integrity.
Next Steps: - Explore 024-security-go.md for security features - Master 025-testing-go.md for testing strategies - Dive into 026-performance-go.md for optimization
Remember: In TuskLang, validation isn't just checkingβit's ensuring your configuration is bulletproof. Use it wisely to build reliable, secure systems.