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

     1  package apiform
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"mime/multipart"
     7  	"net/textproto"
     8  	"path"
     9  	"reflect"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/anthropics/anthropic-sdk-go/packages/param"
    17  )
    18  
    19  var encoders sync.Map // map[encoderEntry]encoderFunc
    20  
    21  func Marshal(value any, writer *multipart.Writer) error {
    22  	e := &encoder{
    23  		dateFormat: time.RFC3339,
    24  		arrayFmt:   "brackets",
    25  	}
    26  	return e.marshal(value, writer)
    27  }
    28  
    29  func MarshalRoot(value any, writer *multipart.Writer) error {
    30  	e := &encoder{
    31  		root:       true,
    32  		dateFormat: time.RFC3339,
    33  		arrayFmt:   "brackets",
    34  	}
    35  	return e.marshal(value, writer)
    36  }
    37  
    38  func MarshalWithSettings(value any, writer *multipart.Writer, arrayFormat string) error {
    39  	e := &encoder{
    40  		arrayFmt:   arrayFormat,
    41  		dateFormat: time.RFC3339,
    42  	}
    43  	return e.marshal(value, writer)
    44  }
    45  
    46  type encoder struct {
    47  	arrayFmt   string
    48  	dateFormat string
    49  	root       bool
    50  }
    51  
    52  type encoderFunc func(key string, value reflect.Value, writer *multipart.Writer) error
    53  
    54  type encoderField struct {
    55  	tag parsedStructTag
    56  	fn  encoderFunc
    57  	idx []int
    58  }
    59  
    60  type encoderEntry struct {
    61  	typ        reflect.Type
    62  	dateFormat string
    63  	arrayFmt   string
    64  	root       bool
    65  }
    66  
    67  func (e *encoder) marshal(value any, writer *multipart.Writer) error {
    68  	val := reflect.ValueOf(value)
    69  	if !val.IsValid() {
    70  		return nil
    71  	}
    72  	typ := val.Type()
    73  	enc := e.typeEncoder(typ)
    74  	return enc("", val, writer)
    75  }
    76  
    77  func (e *encoder) typeEncoder(t reflect.Type) encoderFunc {
    78  	entry := encoderEntry{
    79  		typ:        t,
    80  		dateFormat: e.dateFormat,
    81  		arrayFmt:   e.arrayFmt,
    82  		root:       e.root,
    83  	}
    84  
    85  	if fi, ok := encoders.Load(entry); ok {
    86  		return fi.(encoderFunc)
    87  	}
    88  
    89  	// To deal with recursive types, populate the map with an
    90  	// indirect func before we build it. This type waits on the
    91  	// real func (f) to be ready and then calls it. This indirect
    92  	// func is only used for recursive types.
    93  	var (
    94  		wg sync.WaitGroup
    95  		f  encoderFunc
    96  	)
    97  	wg.Add(1)
    98  	fi, loaded := encoders.LoadOrStore(entry, encoderFunc(func(key string, v reflect.Value, writer *multipart.Writer) error {
    99  		wg.Wait()
   100  		return f(key, v, writer)
   101  	}))
   102  	if loaded {
   103  		return fi.(encoderFunc)
   104  	}
   105  
   106  	// Compute the real encoder and replace the indirect func with it.
   107  	f = e.newTypeEncoder(t)
   108  	wg.Done()
   109  	encoders.Store(entry, f)
   110  	return f
   111  }
   112  
   113  func (e *encoder) newTypeEncoder(t reflect.Type) encoderFunc {
   114  	if t.ConvertibleTo(reflect.TypeOf(time.Time{})) {
   115  		return e.newTimeTypeEncoder()
   116  	}
   117  	if t.Implements(reflect.TypeOf((*io.Reader)(nil)).Elem()) {
   118  		return e.newReaderTypeEncoder()
   119  	}
   120  	e.root = false
   121  	switch t.Kind() {
   122  	case reflect.Pointer:
   123  		inner := t.Elem()
   124  
   125  		innerEncoder := e.typeEncoder(inner)
   126  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   127  			if !v.IsValid() || v.IsNil() {
   128  				return nil
   129  			}
   130  			return innerEncoder(key, v.Elem(), writer)
   131  		}
   132  	case reflect.Struct:
   133  		return e.newStructTypeEncoder(t)
   134  	case reflect.Slice, reflect.Array:
   135  		return e.newArrayTypeEncoder(t)
   136  	case reflect.Map:
   137  		return e.newMapEncoder(t)
   138  	case reflect.Interface:
   139  		return e.newInterfaceEncoder()
   140  	default:
   141  		return e.newPrimitiveTypeEncoder(t)
   142  	}
   143  }
   144  
   145  func (e *encoder) newPrimitiveTypeEncoder(t reflect.Type) encoderFunc {
   146  	switch t.Kind() {
   147  	// Note that we could use `gjson` to encode these types but it would complicate our
   148  	// code more and this current code shouldn't cause any issues
   149  	case reflect.String:
   150  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   151  			return writer.WriteField(key, v.String())
   152  		}
   153  	case reflect.Bool:
   154  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   155  			if v.Bool() {
   156  				return writer.WriteField(key, "true")
   157  			}
   158  			return writer.WriteField(key, "false")
   159  		}
   160  	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
   161  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   162  			return writer.WriteField(key, strconv.FormatInt(v.Int(), 10))
   163  		}
   164  	case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   165  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   166  			return writer.WriteField(key, strconv.FormatUint(v.Uint(), 10))
   167  		}
   168  	case reflect.Float32:
   169  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   170  			return writer.WriteField(key, strconv.FormatFloat(v.Float(), 'f', -1, 32))
   171  		}
   172  	case reflect.Float64:
   173  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   174  			return writer.WriteField(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
   175  		}
   176  	default:
   177  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   178  			return fmt.Errorf("unknown type received at primitive encoder: %s", t.String())
   179  		}
   180  	}
   181  }
   182  
   183  func (e *encoder) newArrayTypeEncoder(t reflect.Type) encoderFunc {
   184  	itemEncoder := e.typeEncoder(t.Elem())
   185  	keyFn := e.arrayKeyEncoder()
   186  	if e.arrayFmt == "comma" {
   187  		return func(key string, v reflect.Value, writer *multipart.Writer) error {
   188  			if v.Len() == 0 {
   189  				return nil
   190  			}
   191  			elements := make([]string, v.Len())
   192  			for i := 0; i < v.Len(); i++ {
   193  				elements[i] = fmt.Sprint(v.Index(i).Interface())
   194  			}
   195  			return writer.WriteField(key, strings.Join(elements, ","))
   196  		}
   197  	}
   198  	return func(key string, v reflect.Value, writer *multipart.Writer) error {
   199  		if keyFn == nil {
   200  			return fmt.Errorf("apiform: unsupported array format")
   201  		}
   202  		for i := 0; i < v.Len(); i++ {
   203  			err := itemEncoder(keyFn(key, i), v.Index(i), writer)
   204  			if err != nil {
   205  				return err
   206  			}
   207  		}
   208  		return nil
   209  	}
   210  }
   211  
   212  func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc {
   213  	if t.Implements(reflect.TypeOf((*param.Optional)(nil)).Elem()) {
   214  		return e.newRichFieldTypeEncoder(t)
   215  	}
   216  
   217  	for i := 0; i < t.NumField(); i++ {
   218  		if t.Field(i).Type == paramUnionType && t.Field(i).Anonymous {
   219  			return e.newStructUnionTypeEncoder(t)
   220  		}
   221  	}
   222  
   223  	encoderFields := []encoderField{}
   224  	extraEncoder := (*encoderField)(nil)
   225  
   226  	// This helper allows us to recursively collect field encoders into a flat
   227  	// array. The parameter `index` keeps track of the access patterns necessary
   228  	// to get to some field.
   229  	var collectEncoderFields func(r reflect.Type, index []int)
   230  	collectEncoderFields = func(r reflect.Type, index []int) {
   231  		for i := 0; i < r.NumField(); i++ {
   232  			idx := append(index, i)
   233  			field := t.FieldByIndex(idx)
   234  			if !field.IsExported() {
   235  				continue
   236  			}
   237  			// If this is an embedded struct, traverse one level deeper to extract
   238  			// the field and get their encoders as well.
   239  			if field.Anonymous {
   240  				collectEncoderFields(field.Type, idx)
   241  				continue
   242  			}
   243  			// If json tag is not present, then we skip, which is intentionally
   244  			// different behavior from the stdlib.
   245  			ptag, ok := parseFormStructTag(field)
   246  			if !ok {
   247  				continue
   248  			}
   249  			// We only want to support unexported field if they're tagged with
   250  			// `extras` because that field shouldn't be part of the public API. We
   251  			// also want to only keep the top level extras
   252  			if ptag.extras && len(index) == 0 {
   253  				extraEncoder = &encoderField{ptag, e.typeEncoder(field.Type.Elem()), idx}
   254  				continue
   255  			}
   256  			if ptag.name == "-" || ptag.name == "" {
   257  				continue
   258  			}
   259  
   260  			dateFormat, ok := parseFormatStructTag(field)
   261  			oldFormat := e.dateFormat
   262  			if ok {
   263  				switch dateFormat {
   264  				case "date-time":
   265  					e.dateFormat = time.RFC3339
   266  				case "date":
   267  					e.dateFormat = "2006-01-02"
   268  				}
   269  			}
   270  
   271  			var encoderFn encoderFunc
   272  			if ptag.omitzero {
   273  				typeEncoderFn := e.typeEncoder(field.Type)
   274  				encoderFn = func(key string, value reflect.Value, writer *multipart.Writer) error {
   275  					if value.IsZero() {
   276  						return nil
   277  					}
   278  					return typeEncoderFn(key, value, writer)
   279  				}
   280  			} else if ptag.defaultValue != nil {
   281  				typeEncoderFn := e.typeEncoder(field.Type)
   282  				encoderFn = func(key string, value reflect.Value, writer *multipart.Writer) error {
   283  					if value.IsZero() {
   284  						return typeEncoderFn(key, reflect.ValueOf(ptag.defaultValue), writer)
   285  					}
   286  					return typeEncoderFn(key, value, writer)
   287  				}
   288  			} else {
   289  				encoderFn = e.typeEncoder(field.Type)
   290  			}
   291  			encoderFields = append(encoderFields, encoderField{ptag, encoderFn, idx})
   292  			e.dateFormat = oldFormat
   293  		}
   294  	}
   295  	collectEncoderFields(t, []int{})
   296  
   297  	// Ensure deterministic output by sorting by lexicographic order
   298  	sort.Slice(encoderFields, func(i, j int) bool {
   299  		return encoderFields[i].tag.name < encoderFields[j].tag.name
   300  	})
   301  
   302  	return func(key string, value reflect.Value, writer *multipart.Writer) error {
   303  		keyFn := e.objKeyEncoder(key)
   304  		for _, ef := range encoderFields {
   305  			field := value.FieldByIndex(ef.idx)
   306  			err := ef.fn(keyFn(ef.tag.name), field, writer)
   307  			if err != nil {
   308  				return err
   309  			}
   310  		}
   311  
   312  		if extraEncoder != nil {
   313  			err := e.encodeMapEntries(key, value.FieldByIndex(extraEncoder.idx), writer)
   314  			if err != nil {
   315  				return err
   316  			}
   317  		}
   318  
   319  		return nil
   320  	}
   321  }
   322  
   323  var paramUnionType = reflect.TypeOf((*param.APIUnion)(nil)).Elem()
   324  
   325  func (e *encoder) newStructUnionTypeEncoder(t reflect.Type) encoderFunc {
   326  	var fieldEncoders []encoderFunc
   327  	for i := 0; i < t.NumField(); i++ {
   328  		field := t.Field(i)
   329  		if field.Type == paramUnionType && field.Anonymous {
   330  			fieldEncoders = append(fieldEncoders, nil)
   331  			continue
   332  		}
   333  		fieldEncoders = append(fieldEncoders, e.typeEncoder(field.Type))
   334  	}
   335  
   336  	return func(key string, value reflect.Value, writer *multipart.Writer) error {
   337  		for i := 0; i < t.NumField(); i++ {
   338  			if value.Field(i).Type() == paramUnionType {
   339  				continue
   340  			}
   341  			if !value.Field(i).IsZero() {
   342  				return fieldEncoders[i](key, value.Field(i), writer)
   343  			}
   344  		}
   345  		return fmt.Errorf("apiform: union %s has no field set", t.String())
   346  	}
   347  }
   348  
   349  func (e *encoder) newTimeTypeEncoder() encoderFunc {
   350  	format := e.dateFormat
   351  	return func(key string, value reflect.Value, writer *multipart.Writer) error {
   352  		return writer.WriteField(key, value.Convert(reflect.TypeOf(time.Time{})).Interface().(time.Time).Format(format))
   353  	}
   354  }
   355  
   356  func (e encoder) newInterfaceEncoder() encoderFunc {
   357  	return func(key string, value reflect.Value, writer *multipart.Writer) error {
   358  		value = value.Elem()
   359  		if !value.IsValid() {
   360  			return nil
   361  		}
   362  		return e.typeEncoder(value.Type())(key, value, writer)
   363  	}
   364  }
   365  
   366  var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
   367  
   368  func escapeQuotes(s string) string {
   369  	return quoteEscaper.Replace(s)
   370  }
   371  
   372  func (e *encoder) newReaderTypeEncoder() encoderFunc {
   373  	return func(key string, value reflect.Value, writer *multipart.Writer) error {
   374  		reader, ok := value.Convert(reflect.TypeOf((*io.Reader)(nil)).Elem()).Interface().(io.Reader)
   375  		if !ok {
   376  			return nil
   377  		}
   378  		filename := "anonymous_file"
   379  		contentType := "application/octet-stream"
   380  		if named, ok := reader.(interface{ Filename() string }); ok {
   381  			filename = named.Filename()
   382  		} else if named, ok := reader.(interface{ Name() string }); ok {
   383  			filename = path.Base(named.Name())
   384  		}
   385  		if typed, ok := reader.(interface{ ContentType() string }); ok {
   386  			contentType = typed.ContentType()
   387  		}
   388  
   389  		// Below is taken almost 1-for-1 from [multipart.CreateFormFile]
   390  		h := make(textproto.MIMEHeader)
   391  		h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, escapeQuotes(key), escapeQuotes(filename)))
   392  		h.Set("Content-Type", contentType)
   393  		filewriter, err := writer.CreatePart(h)
   394  		if err != nil {
   395  			return err
   396  		}
   397  		_, err = io.Copy(filewriter, reader)
   398  		return err
   399  	}
   400  }
   401  
   402  func (e encoder) arrayKeyEncoder() func(string, int) string {
   403  	var keyFn func(string, int) string
   404  	switch e.arrayFmt {
   405  	case "comma", "repeat":
   406  		keyFn = func(k string, _ int) string { return k }
   407  	case "brackets":
   408  		keyFn = func(key string, _ int) string { return key + "[]" }
   409  	case "indices:dots":
   410  		keyFn = func(k string, i int) string {
   411  			if k == "" {
   412  				return strconv.Itoa(i)
   413  			}
   414  			return k + "." + strconv.Itoa(i)
   415  		}
   416  	case "indices:brackets":
   417  		keyFn = func(k string, i int) string {
   418  			if k == "" {
   419  				return strconv.Itoa(i)
   420  			}
   421  			return k + "[" + strconv.Itoa(i) + "]"
   422  		}
   423  	}
   424  	return keyFn
   425  }
   426  
   427  func (e encoder) objKeyEncoder(parent string) func(string) string {
   428  	if parent == "" {
   429  		return func(child string) string { return child }
   430  	}
   431  	switch e.arrayFmt {
   432  	case "brackets":
   433  		return func(child string) string { return parent + "[" + child + "]" }
   434  	default:
   435  		return func(child string) string { return parent + "." + child }
   436  	}
   437  }
   438  
   439  // Given a []byte of json (may either be an empty object or an object that already contains entries)
   440  // encode all of the entries in the map to the json byte array.
   441  func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipart.Writer) error {
   442  	type mapPair struct {
   443  		key   string
   444  		value reflect.Value
   445  	}
   446  
   447  	pairs := []mapPair{}
   448  
   449  	iter := v.MapRange()
   450  	for iter.Next() {
   451  		if iter.Key().Type().Kind() == reflect.String {
   452  			pairs = append(pairs, mapPair{key: iter.Key().String(), value: iter.Value()})
   453  		} else {
   454  			return fmt.Errorf("cannot encode a map with a non string key")
   455  		}
   456  	}
   457  
   458  	// Ensure deterministic output
   459  	sort.Slice(pairs, func(i, j int) bool {
   460  		return pairs[i].key < pairs[j].key
   461  	})
   462  
   463  	elementEncoder := e.typeEncoder(v.Type().Elem())
   464  	keyFn := e.objKeyEncoder(key)
   465  	for _, p := range pairs {
   466  		err := elementEncoder(keyFn(p.key), p.value, writer)
   467  		if err != nil {
   468  			return err
   469  		}
   470  	}
   471  
   472  	return nil
   473  }
   474  
   475  func (e *encoder) newMapEncoder(_ reflect.Type) encoderFunc {
   476  	return func(key string, value reflect.Value, writer *multipart.Writer) error {
   477  		return e.encodeMapEntries(key, value, writer)
   478  	}
   479  }
   480  
   481  func WriteExtras(writer *multipart.Writer, extras map[string]any) (err error) {
   482  	for k, v := range extras {
   483  		str, ok := v.(string)
   484  		if !ok {
   485  			break
   486  		}
   487  		err = writer.WriteField(k, str)
   488  		if err != nil {
   489  			break
   490  		}
   491  	}
   492  	return err
   493  }