67 lines
1.7 KiB
Go
67 lines
1.7 KiB
Go
package domain
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// languageRegex validates ISO 639-1 language codes (two lowercase letters).
|
|
var languageRegex = regexp.MustCompile(`^[a-z]{2}$`)
|
|
|
|
// AllowedKeys defines the valid preference keys and their allowed values.
|
|
// An empty slice means the value is validated by a custom rule (e.g., regex).
|
|
var AllowedKeys = map[string][]string{
|
|
"theme": {"light", "dark", "system"},
|
|
"language": {}, // validated via languageRegex
|
|
"notifications_enabled": {"true", "false"},
|
|
}
|
|
|
|
// Preference represents a single user preference key-value pair.
|
|
type Preference struct {
|
|
UserID string
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
// Validate checks that Key is known and Value is valid for that key.
|
|
func (p *Preference) Validate() error {
|
|
if err := ValidateKey(p.Key); err != nil {
|
|
return err
|
|
}
|
|
return ValidateValue(p.Key, p.Value)
|
|
}
|
|
|
|
// ValidateKey checks if a key is in the allowed set.
|
|
func ValidateKey(key string) error {
|
|
if _, ok := AllowedKeys[key]; !ok {
|
|
return fmt.Errorf("%w: %s", ErrUnknownKey, key)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateValue checks if a value is valid for the given key.
|
|
func ValidateValue(key, value string) error {
|
|
allowed, ok := AllowedKeys[key]
|
|
if !ok {
|
|
return fmt.Errorf("%w: %s", ErrUnknownKey, key)
|
|
}
|
|
|
|
// Language uses regex validation
|
|
if key == "language" {
|
|
if !languageRegex.MatchString(value) {
|
|
return fmt.Errorf("%w for key '%s': must be an ISO 639-1 code (e.g., en, fr)", ErrInvalidValue, key)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Enum validation for keys with explicit allowed values
|
|
for _, v := range allowed {
|
|
if v == value {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("%w '%s' for key '%s': allowed values are [%s]", ErrInvalidValue, value, key, strings.Join(allowed, ", "))
|
|
}
|