github.com/anthropics/anthropic-sdk-go@v1.70.1/internal/apijson/union.go (about)

     1  package apijson
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"sync"
     7  
     8  	"github.com/anthropics/anthropic-sdk-go/packages/param"
     9  
    10  	"github.com/tidwall/gjson"
    11  )
    12  
    13  var apiUnionType = reflect.TypeOf(param.APIUnion{})
    14  var apiObjectType = reflect.TypeOf(param.APIObject{})
    15  
    16  var nativeTypeCache sync.Map // map[reflect.Type]bool
    17  
    18  // CustomUnmarshaler indicates that a type is not "apijson native".
    19  // apijson native types use the custom [json.Unmarshaler] only as an entry
    20  // point, and therefore are skipped when nested.
    21  type CustomUnmarshaler interface {
    22  	UnmarshalAPIJSON([]byte) error
    23  }
    24  
    25  var customUnmarshalerType = reflect.TypeOf((*CustomUnmarshaler)(nil)).Elem()
    26  
    27  func implementsCustomUnmarshaler(t reflect.Type) bool {
    28  	return t.Implements(customUnmarshalerType) || reflect.PointerTo(t).Implements(customUnmarshalerType)
    29  }
    30  
    31  // isApijsonNative reports whether t is a generated struct whose
    32  // UnmarshalJSON is the trivial delegation to [UnmarshalRoot], therefore
    33  // the UnmarshalJSON function can be skipped for nested values.
    34  func isApijsonNative(t reflect.Type) bool {
    35  	if cached, ok := nativeTypeCache.Load(t); ok {
    36  		return cached.(bool)
    37  	}
    38  	native := computeApijsonNative(t)
    39  	nativeTypeCache.Store(t, native)
    40  	return native
    41  }
    42  
    43  func computeApijsonNative(t reflect.Type) bool {
    44  	for t.Kind() == reflect.Pointer {
    45  		t = t.Elem()
    46  	}
    47  	if t.Kind() != reflect.Struct {
    48  		return false
    49  	}
    50  	if implementsCustomUnmarshaler(t) {
    51  		return false
    52  	}
    53  	if isParamStruct(t) || isStructUnion(t) {
    54  		return true
    55  	}
    56  	if f, ok := t.FieldByName("JSON"); ok && f.Type.Kind() == reflect.Struct {
    57  		if _, ok := f.Type.FieldByName("raw"); ok {
    58  			return true
    59  		}
    60  	}
    61  	return false
    62  }
    63  
    64  var paramObjectIdxCache sync.Map // map[reflect.Type]int
    65  
    66  func paramObjectIndex(t reflect.Type) int {
    67  	return paramEmbedIndex(t, apiObjectType, &paramObjectIdxCache)
    68  }
    69  
    70  func paramEmbedIndex(t reflect.Type, embed reflect.Type, cache *sync.Map) int {
    71  	if cached, ok := cache.Load(t); ok {
    72  		return cached.(int)
    73  	}
    74  	idx := -1
    75  	if t.Kind() == reflect.Struct {
    76  		for i := 0; i < t.NumField(); i++ {
    77  			f := t.Field(i)
    78  			if f.Anonymous && f.Type == embed {
    79  				idx = i
    80  				break
    81  			}
    82  		}
    83  	}
    84  	cache.Store(t, idx)
    85  	return idx
    86  }
    87  
    88  // isParamStruct reports whether t is a generated request param struct.
    89  func isParamStruct(t reflect.Type) bool {
    90  	return paramObjectIndex(t) >= 0
    91  }
    92  
    93  func isStructUnion(t reflect.Type) bool {
    94  	if t.Kind() != reflect.Struct {
    95  		return false
    96  	}
    97  	for i := 0; i < t.NumField(); i++ {
    98  		if t.Field(i).Type == apiUnionType && t.Field(i).Anonymous {
    99  			return true
   100  		}
   101  	}
   102  	return false
   103  }
   104  
   105  func RegisterDiscriminatedUnion[T any](key string, mappings map[string]reflect.Type) {
   106  	var t T
   107  	entry := unionEntry{
   108  		discriminatorKey: key,
   109  		variants:         []UnionVariant{},
   110  	}
   111  	for k, typ := range mappings {
   112  		entry.variants = append(entry.variants, UnionVariant{
   113  			DiscriminatorValue: k,
   114  			Type:               typ,
   115  		})
   116  	}
   117  	unionRegistry[reflect.TypeOf(t)] = entry
   118  }
   119  
   120  // deriveDiscriminator picks the `default:`-tagged field whose values
   121  // route the most variants uniquely, and returns that field's JSON
   122  // name plus a value→variant-index map.
   123  func deriveDiscriminator(variants []reflect.StructField) (key string, index map[string]int) {
   124  	byKey := map[string]map[string]int{}
   125  	for idx, v := range variants {
   126  		t := v.Type
   127  		for t.Kind() == reflect.Ptr {
   128  			t = t.Elem()
   129  		}
   130  		if t.Kind() != reflect.Struct {
   131  			continue
   132  		}
   133  		for i := 0; i < t.NumField(); i++ {
   134  			ptag, ok := parseJSONStructTag(t.Field(i))
   135  			if !ok {
   136  				continue
   137  			}
   138  			def, ok := ptag.defaultValue.(string)
   139  			if !ok {
   140  				continue
   141  			}
   142  			m := byKey[ptag.name]
   143  			if m == nil {
   144  				m = map[string]int{}
   145  				byKey[ptag.name] = m
   146  			}
   147  			if _, dup := m[def]; dup {
   148  				m[def] = -1
   149  			} else {
   150  				m[def] = idx
   151  			}
   152  		}
   153  	}
   154  	for k, m := range byKey {
   155  		usable := map[string]int{}
   156  		for v, idx := range m {
   157  			if idx >= 0 {
   158  				usable[v] = idx
   159  			}
   160  		}
   161  		if len(usable) > len(index) || (len(usable) == len(index) && k < key) {
   162  			key, index = k, usable
   163  		}
   164  	}
   165  	return key, index
   166  }
   167  
   168  func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc {
   169  	type variantDecoder struct {
   170  		decoder decoderFunc
   171  		field   reflect.StructField
   172  	}
   173  	var decoders []variantDecoder
   174  	var fields []reflect.StructField
   175  	for i := 0; i < t.NumField(); i++ {
   176  		field := t.Field(i)
   177  		if field.Anonymous && field.Type == apiUnionType {
   178  			continue
   179  		}
   180  		decoders = append(decoders, variantDecoder{d.typeDecoder(field.Type), field})
   181  		fields = append(fields, field)
   182  	}
   183  
   184  	// To more performantly pick the correct variant, derive a
   185  	// discriminator from the `default:` tags.
   186  	discrimKey, discrimIndex := deriveDiscriminator(fields)
   187  
   188  	type discriminatedDecoder struct {
   189  		variantDecoder
   190  		discriminator any
   191  	}
   192  	discriminatedDecoders := []discriminatedDecoder{}
   193  	unionEntry, discriminated := unionRegistry[t]
   194  	for _, variant := range unionEntry.variants {
   195  		// For each union variant, find a matching decoder and save it
   196  		for _, decoder := range decoders {
   197  			if decoder.field.Type.Elem() == variant.Type {
   198  				discriminatedDecoders = append(discriminatedDecoders, discriminatedDecoder{
   199  					decoder,
   200  					variant.DiscriminatorValue,
   201  				})
   202  				break
   203  			}
   204  		}
   205  	}
   206  
   207  	return func(n gjson.Result, v reflect.Value, state *decoderState) error {
   208  		if discriminated && n.Type == gjson.JSON && len(unionEntry.discriminatorKey) != 0 {
   209  			discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
   210  			for _, decoder := range discriminatedDecoders {
   211  				if discriminator == decoder.discriminator {
   212  					inner := v.FieldByIndex(decoder.field.Index)
   213  					return decoder.decoder(n, inner, state)
   214  				}
   215  			}
   216  			return errors.New("apijson: was not able to find discriminated union variant")
   217  		}
   218  
   219  		scanned := discrimKey != "" && n.Type == gjson.JSON
   220  		if scanned {
   221  			if i, ok := discrimIndex[n.Get(EscapeSJSONKey(discrimKey)).String()]; ok {
   222  				sub := decoderState{strict: state.strict}
   223  				inner := v.FieldByIndex(decoders[i].field.Index)
   224  				if decoders[i].decoder(n, inner, &sub) == nil {
   225  					state.exactness.absorb(sub.exactness)
   226  					return nil
   227  				}
   228  			}
   229  		}
   230  
   231  		var best exactness
   232  		bestVariant := -1
   233  		for i, decoder := range decoders {
   234  			// Pointers are used to discern JSON object variants from value variants
   235  			if n.Type != gjson.JSON && decoder.field.Type.Kind() == reflect.Ptr {
   236  				continue
   237  			}
   238  
   239  			sub := decoderState{strict: state.strict}
   240  			inner := v.FieldByIndex(decoder.field.Index)
   241  			if err := decoder.decoder(n, inner, &sub); err != nil {
   242  				continue
   243  			}
   244  			if bestVariant < 0 || sub.exactness.betterThan(best) {
   245  				best, bestVariant = sub.exactness, i
   246  				// A perfect fit cannot be beaten once the discriminator
   247  				// scan (or the fit itself) has ruled out a later hit.
   248  				if best.perfect() && (scanned || best.constHit()) {
   249  					break
   250  				}
   251  			}
   252  		}
   253  
   254  		if bestVariant < 0 {
   255  			return errors.New("apijson: was not able to coerce type as union")
   256  		}
   257  
   258  		if state.strict && !best.perfect() {
   259  			return errors.New("apijson: was not able to coerce type as union strictly")
   260  		}
   261  		state.exactness.absorb(best)
   262  
   263  		for i := 0; i < len(decoders); i++ {
   264  			if i == bestVariant {
   265  				continue
   266  			}
   267  			v.FieldByIndex(decoders[i].field.Index).SetZero()
   268  		}
   269  
   270  		return nil
   271  	}
   272  }
   273  
   274  // newUnionDecoder returns a decoderFunc that deserializes into a union using an
   275  // algorithm roughly similar to Pydantic's [smart algorithm].
   276  //
   277  // Conceptually this is equivalent to choosing the best schema based on how 'exact'
   278  // the deserialization is for each of the schemas.
   279  //
   280  // If there is a tie in the level of exactness, then the tie is broken
   281  // left-to-right.
   282  //
   283  // [smart algorithm]: https://docs.pydantic.dev/latest/concepts/unions/#smart-mode
   284  func (d *decoderBuilder) newUnionDecoder(t reflect.Type) decoderFunc {
   285  	unionEntry, ok := unionRegistry[t]
   286  	if !ok {
   287  		panic("apijson: couldn't find union of type " + t.String() + " in union registry")
   288  	}
   289  	decoders := []decoderFunc{}
   290  	for _, variant := range unionEntry.variants {
   291  		decoder := d.typeDecoder(variant.Type)
   292  		decoders = append(decoders, decoder)
   293  	}
   294  	return func(n gjson.Result, v reflect.Value, state *decoderState) error {
   295  		// If there is a discriminator match, circumvent the exactness logic entirely
   296  		for idx, variant := range unionEntry.variants {
   297  			decoder := decoders[idx]
   298  			if variant.TypeFilter != n.Type {
   299  				continue
   300  			}
   301  
   302  			if len(unionEntry.discriminatorKey) != 0 {
   303  				discriminatorValue := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
   304  				if discriminatorValue == variant.DiscriminatorValue {
   305  					inner := reflect.New(variant.Type).Elem()
   306  					err := decoder(n, inner, state)
   307  					v.Set(inner)
   308  					return err
   309  				}
   310  			}
   311  		}
   312  
   313  		var bestExactness exactness
   314  		found := false
   315  		for idx, variant := range unionEntry.variants {
   316  			decoder := decoders[idx]
   317  			if variant.TypeFilter != n.Type {
   318  				continue
   319  			}
   320  			sub := decoderState{strict: state.strict}
   321  			inner := reflect.New(variant.Type).Elem()
   322  			err := decoder(n, inner, &sub)
   323  			if err != nil {
   324  				continue
   325  			}
   326  			if !found || sub.exactness.betterThan(bestExactness) {
   327  				v.Set(inner)
   328  				bestExactness = sub.exactness
   329  				found = true
   330  				if bestExactness.constHit() && bestExactness.perfect() {
   331  					return nil
   332  				}
   333  			}
   334  		}
   335  
   336  		if !found {
   337  			return errors.New("apijson: was not able to coerce type as union")
   338  		}
   339  
   340  		if state.strict && !bestExactness.perfect() {
   341  			return errors.New("apijson: was not able to coerce type as union strictly")
   342  		}
   343  		state.exactness.absorb(bestExactness)
   344  
   345  		return nil
   346  	}
   347  }