Something went wrong. Try again.
cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 🍃
charm leaflet readability golang
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187package services
import ( "fmt" "net/url" "regexp" "slices" "strings" "time")
var ( emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) dateFormats = []string{ "2006-01-02", "2006-01-02T15:04:05Z", "2006-01-02T15:04:05-07:00", "2006-01-02 15:04:05", })
// ValidationError represents a validation errortype ValidationError struct { Field string Message string}
func (e ValidationError) Error() string { return fmt.Sprintf("validation error for field '%s': %s", e.Field, e.Message)}
func NewValidationError(f, m string) ValidationError { return ValidationError{Field: f, Message: m}}
// ValidationErrors represents multiple validation errorstype ValidationErrors []ValidationError
func (e ValidationErrors) Error() string { if len(e) == 0 { return "no validation errors" }
if len(e) == 1 { return e[0].Error() }
var messages []string for _, err := range e { messages = append(messages, err.Error()) } return fmt.Sprintf("multiple validation errors: %s", strings.Join(messages, "; "))}
// RequiredString validates that a string field is not emptyfunc RequiredString(name, value string) error { if strings.TrimSpace(value) == "" { return NewValidationError(name, "is required and cannot be empty") } return nil}
// ValidURL validates that a string is a valid URLfunc ValidURL(name, value string) error { if value == "" { return nil }
parsed, err := url.Parse(value) if err != nil { return ValidationError{Field: name, Message: "must be a valid URL"} }
if parsed.Scheme != "http" && parsed.Scheme != "https" { return NewValidationError(name, "must use http or https scheme") }
return nil}
// ValidEmail validates that a string is a valid email addressfunc ValidEmail(name, value string) error { if value == "" { return nil }
if !emailRegex.MatchString(value) { return NewValidationError(name, "must be a valid email address") }
return nil}
// StringLength validates string length constraintsfunc StringLength(name, value string, min, max int) error { length := len(strings.TrimSpace(value)) if min > 0 && length < min { return NewValidationError(name, fmt.Sprintf("must be at least %d characters long", min)) } if max > 0 && length > max { return NewValidationError(name, fmt.Sprintf("must not exceed %d characters", max)) } return nil}
// ValidDate validates that a string can be parsed as a date in supported formatsfunc ValidDate(name, value string) error { if value == "" { return nil }
for _, format := range dateFormats { if _, err := time.Parse(format, value); err == nil { return nil } } return NewValidationError(name, "must be a valid date (YYYY-MM-DD, YYYY-MM-DDTHH:MM:SSZ, etc.)")}
// PositiveID validates that an ID is positivefunc PositiveID(name string, value int64) error { if value <= 0 { return NewValidationError(name, "must be a positive integer") } return nil}
// ValidEnum validates that a value is one of the allowed enum valuesfunc ValidEnum(name, value string, allowedValues []string) error { if value == "" { return nil } if slices.Contains(allowedValues, value) { return nil } return NewValidationError(name, fmt.Sprintf("must be one of: %s", strings.Join(allowedValues, ", ")))}
// ValidFilePath validates that a string looks like a valid file pathfunc ValidFilePath(name, value string) error { if value == "" { return nil } if strings.Contains(value, "..") { return NewValidationError(name, "cannot contain '..' path traversal") } if strings.ContainsAny(value, "<>:\"|?*") { return NewValidationError(name, "contains invalid characters") } return nil}
// Validator provides a fluent interface for validationtype Validator struct { errors ValidationErrors}
// NewValidator creates a new validator instancefunc NewValidator() *Validator { return &Validator{}}
// Check adds a validation checkfunc (v *Validator) Check(err error) *Validator { if err != nil { if valErr, ok := err.(ValidationError); ok { v.errors = append(v.errors, valErr) } else { v.errors = append(v.errors, NewValidationError("unknown", err.Error())) } } return v}
// IsValid returns true if no validation errors occurredfunc (v *Validator) IsValid() bool { return len(v.errors) == 0}
// Errors returns all validation errorsfunc (v *Validator) Errors() error { if len(v.errors) == 0 { return nil } return v.errors}