package validation import ( "fmt" "slices" "strings" "unicode" ) type StringField struct { Field Value string } func newStringField(key string, g Getter) *StringField { return &StringField{ Value: g.Get(key), Field: newField(key, g), } } // Required enforces a non empty string func (s *StringField) Required() *StringField { if s.Value == "" { s.getter.AddCheck(newFailedCheck( "Field not provided", fmt.Sprintf("%s is required", s.Key), )) } return s } // Optional will skip all validations if value is empty func (s *StringField) Optional() *StringField { if s.Value == "" { s.optional = true } return s } // MaxLength enforces a maximum string length func (s *StringField) MaxLength(length int) *StringField { if len(s.Value) > length && !s.optional { s.getter.AddCheck(newFailedCheck( "Input too long", fmt.Sprintf("%s is too long, max %v chars", s.Key, length), )) } return s } // MinLength enforces a minimum string length func (s *StringField) MinLength(length int) *StringField { if len(s.Value) < length && !s.optional { s.getter.AddCheck(newFailedCheck( "Input too short", fmt.Sprintf("%s is too short, min %v chars", s.Key, length), )) } return s } // AlphaNumeric enforces the string contains only letters and numbers func (s *StringField) AlphaNumeric() *StringField { if s.optional { return s } for _, r := range s.Value { if !unicode.IsLetter(r) && !unicode.IsDigit(r) { s.getter.AddCheck(newFailedCheck( "Invalid characters", fmt.Sprintf("%s must contain only letters and numbers.", s.Key), )) return s } } return s } func (s *StringField) AllowedValues(allowed []string) *StringField { if !slices.Contains(allowed, s.Value) && !s.optional { s.getter.AddCheck(newFailedCheck( "Value not allowed", fmt.Sprintf("%s must be one of: %s", s.Key, strings.Join(allowed, ",")), )) } return s } // ToUpper transforms the string to uppercase func (s *StringField) ToUpper() *StringField { s.Value = strings.ToUpper(s.Value) return s } // ToLower transforms the string to lowercase func (s *StringField) ToLower() *StringField { s.Value = strings.ToLower(s.Value) return s } // TrimSpace removes leading and trailing whitespace func (s *StringField) TrimSpace() *StringField { s.Value = strings.TrimSpace(s.Value) return s } // Replace replaces all occurances of the given substring func (s *StringField) Replace(old, new string) *StringField { s.Value = strings.ReplaceAll(s.Value, old, new) return s }