blob: 141784273a9f837c7a5ed59f310859cadf2f7073 [file]
package httpx
import "strings"
// Constraints accumulates field violations, standing in for the jakarta.validation
// annotations on the Java DTOs. The messages are carried over verbatim so clients see
// the same text.
type Constraints struct {
fields []FieldError
}
// NotBlank records a violation when value is empty or only whitespace, mirroring
// @NotBlank.
func (c *Constraints) NotBlank(field, message string, value string) {
if strings.TrimSpace(value) == "" {
c.Add(field, message)
}
}
// NotBlankPtr is NotBlank for an optional field: a nil pointer is as much a violation
// as a blank string.
func (c *Constraints) NotBlankPtr(field, message string, value *string) {
if value == nil || strings.TrimSpace(*value) == "" {
c.Add(field, message)
}
}
// NotNil records a violation when value is nil, mirroring @NotNull.
func (c *Constraints) NotNil(field, message string, value any) {
if isNil(value) {
c.Add(field, message)
}
}
// Positive records a violation when value is nil or not greater than zero, mirroring
// @NotNull @Positive on wallTimeMinutes.
func (c *Constraints) Positive(field, message string, value *int64) {
if value == nil || *value <= 0 {
c.Add(field, message)
}
}
// Email records a violation when value is present but not plausibly an address,
// mirroring @Email. Like Jakarta's default, a nil or empty value passes.
func (c *Constraints) Email(field, message string, value *string) {
if value == nil || *value == "" {
return
}
at := strings.IndexByte(*value, '@')
if at <= 0 || at == len(*value)-1 || strings.ContainsAny(*value, " \t") {
c.Add(field, message)
}
}
// Add records an arbitrary violation.
func (c *Constraints) Add(field, message string) {
c.fields = append(c.fields, FieldError{Field: field, Message: message})
}
// Nested folds the violations of a nested payload in under a prefix, mirroring @Valid
// on a nested DTO.
func (c *Constraints) Nested(prefix string, v Validator) {
if v == nil || isNil(v) {
return
}
for _, f := range v.Validate() {
c.Add(prefix+"."+f.Field, f.Message)
}
}
// Fields returns everything recorded so far.
func (c *Constraints) Fields() []FieldError { return c.fields }
func isNil(v any) bool {
if v == nil {
return true
}
switch t := v.(type) {
case *string:
return t == nil
case *int64:
return t == nil
case *int32:
return t == nil
case *bool:
return t == nil
}
return false
}