slim/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go
dependabot[bot] 92169241a2
Bump github.com/getkin/kin-openapi from 0.76.0 to 0.131.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.76.0 to 0.131.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.76.0...v0.131.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-version: 0.131.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-15 16:49:42 +00:00

172 lines
6.1 KiB
Go

package openapi3
import (
"fmt"
"math"
"net/netip"
"regexp"
)
type (
// FormatValidator is an interface for custom format validators.
FormatValidator[T any] interface {
Validate(value T) error
}
// StringFormatValidator is a type alias for FormatValidator[string]
StringFormatValidator = FormatValidator[string]
// NumberFormatValidator is a type alias for FormatValidator[float64]
NumberFormatValidator = FormatValidator[float64]
// IntegerFormatValidator is a type alias for FormatValidator[int64]
IntegerFormatValidator = FormatValidator[int64]
)
var (
// SchemaStringFormats is a map of custom string format validators.
SchemaStringFormats = make(map[string]StringFormatValidator)
// SchemaNumberFormats is a map of custom number format validators.
SchemaNumberFormats = make(map[string]NumberFormatValidator)
// SchemaIntegerFormats is a map of custom integer format validators.
SchemaIntegerFormats = make(map[string]IntegerFormatValidator)
)
const (
// FormatOfStringForUUIDOfRFC4122 is an optional predefined format for UUID v1-v5 as specified by RFC4122
FormatOfStringForUUIDOfRFC4122 = `^(?:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$`
// FormatOfStringForEmail pattern catches only some suspiciously wrong-looking email addresses.
// Use DefineStringFormat(...) if you need something stricter.
FormatOfStringForEmail = `^[^@]+@[^@<>",\s]+$`
// FormatOfStringByte is a regexp for base64-encoded characters, for example, "U3dhZ2dlciByb2Nrcw=="
FormatOfStringByte = `(^$|^[a-zA-Z0-9+/\-_]*=*$)`
// FormatOfStringDate is a RFC3339 date format regexp, for example "2017-07-21".
FormatOfStringDate = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])$`
// FormatOfStringDateTime is a RFC3339 date-time format regexp, for example "2017-07-21T17:32:28Z".
FormatOfStringDateTime = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$`
)
func init() {
DefineStringFormatValidator("byte", NewRegexpFormatValidator(FormatOfStringByte))
DefineStringFormatValidator("date", NewRegexpFormatValidator(FormatOfStringDate))
DefineStringFormatValidator("date-time", NewRegexpFormatValidator(FormatOfStringDateTime))
DefineIntegerFormatValidator("int32", NewRangeFormatValidator(int64(math.MinInt32), int64(math.MaxInt32)))
DefineIntegerFormatValidator("int64", NewRangeFormatValidator(int64(math.MinInt64), int64(math.MaxInt64)))
}
// DefineIPv4Format opts in ipv4 format validation on top of OAS 3 spec
func DefineIPv4Format() {
DefineStringFormatValidator("ipv4", NewIPValidator(true))
}
// DefineIPv6Format opts in ipv6 format validation on top of OAS 3 spec
func DefineIPv6Format() {
DefineStringFormatValidator("ipv6", NewIPValidator(false))
}
type stringRegexpFormatValidator struct {
re *regexp.Regexp
}
func (s stringRegexpFormatValidator) Validate(value string) error {
if !s.re.MatchString(value) {
return fmt.Errorf(`string doesn't match pattern "%s"`, s.re.String())
}
return nil
}
type callbackValidator[T any] struct {
fn func(T) error
}
func (c callbackValidator[T]) Validate(value T) error {
return c.fn(value)
}
type rangeFormat[T int64 | float64] struct {
min, max T
}
func (r rangeFormat[T]) Validate(value T) error {
if value < r.min || value > r.max {
return fmt.Errorf("value should be between %v and %v", r.min, r.max)
}
return nil
}
// NewRangeFormatValidator creates a new FormatValidator that validates the value is within a given range.
func NewRangeFormatValidator[T int64 | float64](min, max T) FormatValidator[T] {
return rangeFormat[T]{min: min, max: max}
}
// NewRegexpFormatValidator creates a new FormatValidator that uses a regular expression to validate the value.
func NewRegexpFormatValidator(pattern string) StringFormatValidator {
re, err := regexp.Compile(pattern)
if err != nil {
err := fmt.Errorf("string regexp format has invalid pattern %q: %w", pattern, err)
panic(err)
}
return stringRegexpFormatValidator{re: re}
}
// NewCallbackValidator creates a new FormatValidator that uses a callback function to validate the value.
func NewCallbackValidator[T any](fn func(T) error) FormatValidator[T] {
return callbackValidator[T]{fn: fn}
}
// DefineStringFormatValidator defines a custom format validator for a given string format.
func DefineStringFormatValidator(name string, validator StringFormatValidator) {
SchemaStringFormats[name] = validator
}
// DefineNumberFormatValidator defines a custom format validator for a given number format.
func DefineNumberFormatValidator(name string, validator NumberFormatValidator) {
SchemaNumberFormats[name] = validator
}
// DefineIntegerFormatValidator defines a custom format validator for a given integer format.
func DefineIntegerFormatValidator(name string, validator IntegerFormatValidator) {
SchemaIntegerFormats[name] = validator
}
// DefineStringFormat defines a regexp pattern for a given string format
//
// Deprecated: Use openapi3.DefineStringFormatValidator(name, NewRegexpFormatValidator(pattern)) instead.
func DefineStringFormat(name string, pattern string) {
DefineStringFormatValidator(name, NewRegexpFormatValidator(pattern))
}
// DefineStringFormatCallback defines a callback function for a given string format
//
// Deprecated: Use openapi3.DefineStringFormatValidator(name, NewCallbackValidator(fn)) instead.
func DefineStringFormatCallback(name string, callback func(string) error) {
DefineStringFormatValidator(name, NewCallbackValidator(callback))
}
// NewIPValidator creates a new FormatValidator that validates the value is an IP address.
func NewIPValidator(isIPv4 bool) FormatValidator[string] {
return callbackValidator[string]{fn: func(ip string) error {
addr, err := netip.ParseAddr(ip)
if err != nil {
return &SchemaError{
Value: ip,
Reason: "Not an IP address",
}
}
if isIPv4 && !addr.Is4() {
return &SchemaError{
Value: ip,
Reason: "Not an IPv4 address (it's IPv6)",
}
}
if !isIPv4 && !addr.Is6() {
return &SchemaError{
Value: ip,
Reason: "Not an IPv6 address (it's IPv4)",
}
}
return nil
}}
}