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  }