github.com/anthropics/anthropic-sdk-go@v1.70.1/internal/apijson/enum.go (about) 1 package apijson 2 3 import ( 4 "fmt" 5 "reflect" 6 "slices" 7 8 "github.com/tidwall/gjson" 9 ) 10 11 /********************/ 12 /* Validating Enums */ 13 /********************/ 14 15 type validationEntry struct { 16 field reflect.StructField 17 legalValues struct { 18 strings []string 19 // 1 represents true, 0 represents false, -1 represents either 20 bools int 21 ints []int64 22 } 23 } 24 25 var validationRegistry = map[reflect.Type][]validationEntry{} 26 27 func RegisterFieldValidator[T any, V string | bool | int | float64](fieldName string, values ...V) { 28 var t T 29 parentType := reflect.TypeOf(t) 30 31 if _, ok := validationRegistry[parentType]; !ok { 32 validationRegistry[parentType] = []validationEntry{} 33 } 34 35 // The following checks run at initialization time, 36 // it is impossible for them to panic if any tests pass. 37 if parentType.Kind() != reflect.Struct { 38 panic(fmt.Sprintf("apijson: cannot initialize validator for non-struct %s", parentType.String())) 39 } 40 41 var field reflect.StructField 42 found := false 43 for i := 0; i < parentType.NumField(); i++ { 44 ptag, ok := parseJSONStructTag(parentType.Field(i)) 45 if ok && ptag.name == fieldName { 46 field = parentType.Field(i) 47 found = true 48 break 49 } 50 } 51 52 if !found { 53 panic(fmt.Sprintf("apijson: cannot find field %s in struct %s", fieldName, parentType.String())) 54 } 55 56 newEntry := validationEntry{field: field} 57 newEntry.legalValues.bools = -1 // default to either 58 59 switch values := any(values).(type) { 60 case []string: 61 newEntry.legalValues.strings = values 62 case []int: 63 newEntry.legalValues.ints = make([]int64, len(values)) 64 for i, value := range values { 65 newEntry.legalValues.ints[i] = int64(value) 66 } 67 case []bool: 68 for i, value := range values { 69 var next int 70 if value { 71 next = 1 72 } 73 if i > 0 && newEntry.legalValues.bools != next { 74 newEntry.legalValues.bools = -1 // accept either 75 break 76 } 77 newEntry.legalValues.bools = next 78 } 79 } 80 81 // Store the information necessary to create a validator, so that we can use it 82 // lazily create the validator function when did. 83 validationRegistry[parentType] = append(validationRegistry[parentType], newEntry) 84 } 85 86 func (state *decoderState) validateString(v reflect.Value) { 87 if state.validator == nil { 88 return 89 } 90 if !slices.Contains(state.validator.legalValues.strings, v.String()) { 91 state.exactness.noteCoercion() 92 } 93 } 94 95 func (state *decoderState) validateInt(v reflect.Value) { 96 if state.validator == nil { 97 return 98 } 99 if !slices.Contains(state.validator.legalValues.ints, v.Int()) { 100 state.exactness.noteCoercion() 101 } 102 } 103 104 func (state *decoderState) validateBool(v reflect.Value) { 105 if state.validator == nil { 106 return 107 } 108 b := v.Bool() 109 if state.validator.legalValues.bools == 1 && !b { 110 state.exactness.noteCoercion() 111 } else if state.validator.legalValues.bools == 0 && b { 112 state.exactness.noteCoercion() 113 } 114 } 115 116 func (state *decoderState) validateOptKind(node gjson.Result, t reflect.Type) { 117 switch node.Type { 118 case gjson.JSON: 119 state.exactness.noteCoercion() 120 case gjson.Null: 121 return 122 case gjson.False, gjson.True: 123 if t.Kind() != reflect.Bool { 124 state.exactness.noteCoercion() 125 } 126 case gjson.Number: 127 switch t.Kind() { 128 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 129 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 130 reflect.Float32, reflect.Float64: 131 return 132 default: 133 state.exactness.noteCoercion() 134 } 135 case gjson.String: 136 if t.Kind() != reflect.String { 137 state.exactness.noteCoercion() 138 } 139 } 140 }