mirror of
https://github.com/openai/openai-go.git
synced 2026-04-02 01:19:53 +09:00
141 lines
3.4 KiB
Go
141 lines
3.4 KiB
Go
package apijson
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"slices"
|
|
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
/********************/
|
|
/* Validating Enums */
|
|
/********************/
|
|
|
|
type validationEntry struct {
|
|
field reflect.StructField
|
|
legalValues struct {
|
|
strings []string
|
|
// 1 represents true, 0 represents false, -1 represents either
|
|
bools int
|
|
ints []int64
|
|
}
|
|
}
|
|
|
|
var validationRegistry = map[reflect.Type][]validationEntry{}
|
|
|
|
func RegisterFieldValidator[T any, V string | bool | int | float64](fieldName string, values ...V) {
|
|
var t T
|
|
parentType := reflect.TypeOf(t)
|
|
|
|
if _, ok := validationRegistry[parentType]; !ok {
|
|
validationRegistry[parentType] = []validationEntry{}
|
|
}
|
|
|
|
// The following checks run at initialization time,
|
|
// it is impossible for them to panic if any tests pass.
|
|
if parentType.Kind() != reflect.Struct {
|
|
panic(fmt.Sprintf("apijson: cannot initialize validator for non-struct %s", parentType.String()))
|
|
}
|
|
|
|
var field reflect.StructField
|
|
found := false
|
|
for i := 0; i < parentType.NumField(); i++ {
|
|
ptag, ok := parseJSONStructTag(parentType.Field(i))
|
|
if ok && ptag.name == fieldName {
|
|
field = parentType.Field(i)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
panic(fmt.Sprintf("apijson: cannot find field %s in struct %s", fieldName, parentType.String()))
|
|
}
|
|
|
|
newEntry := validationEntry{field: field}
|
|
newEntry.legalValues.bools = -1 // default to either
|
|
|
|
switch values := any(values).(type) {
|
|
case []string:
|
|
newEntry.legalValues.strings = values
|
|
case []int:
|
|
newEntry.legalValues.ints = make([]int64, len(values))
|
|
for i, value := range values {
|
|
newEntry.legalValues.ints[i] = int64(value)
|
|
}
|
|
case []bool:
|
|
for i, value := range values {
|
|
var next int
|
|
if value {
|
|
next = 1
|
|
}
|
|
if i > 0 && newEntry.legalValues.bools != next {
|
|
newEntry.legalValues.bools = -1 // accept either
|
|
break
|
|
}
|
|
newEntry.legalValues.bools = next
|
|
}
|
|
}
|
|
|
|
// Store the information necessary to create a validator, so that we can use it
|
|
// lazily create the validator function when did.
|
|
validationRegistry[parentType] = append(validationRegistry[parentType], newEntry)
|
|
}
|
|
|
|
func (state *decoderState) validateString(v reflect.Value) {
|
|
if state.validator == nil {
|
|
return
|
|
}
|
|
if !slices.Contains(state.validator.legalValues.strings, v.String()) {
|
|
state.exactness = loose
|
|
}
|
|
}
|
|
|
|
func (state *decoderState) validateInt(v reflect.Value) {
|
|
if state.validator == nil {
|
|
return
|
|
}
|
|
if !slices.Contains(state.validator.legalValues.ints, v.Int()) {
|
|
state.exactness = loose
|
|
}
|
|
}
|
|
|
|
func (state *decoderState) validateBool(v reflect.Value) {
|
|
if state.validator == nil {
|
|
return
|
|
}
|
|
b := v.Bool()
|
|
if state.validator.legalValues.bools == 1 && !b {
|
|
state.exactness = loose
|
|
} else if state.validator.legalValues.bools == 0 && b {
|
|
state.exactness = loose
|
|
}
|
|
}
|
|
|
|
func (state *decoderState) validateOptKind(node gjson.Result, t reflect.Type) {
|
|
switch node.Type {
|
|
case gjson.JSON:
|
|
state.exactness = loose
|
|
case gjson.Null:
|
|
return
|
|
case gjson.False, gjson.True:
|
|
if t.Kind() != reflect.Bool {
|
|
state.exactness = loose
|
|
}
|
|
case gjson.Number:
|
|
switch t.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
reflect.Float32, reflect.Float64:
|
|
return
|
|
default:
|
|
state.exactness = loose
|
|
}
|
|
case gjson.String:
|
|
if t.Kind() != reflect.String {
|
|
state.exactness = loose
|
|
}
|
|
}
|
|
}
|