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

     1  package param
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  	"time"
     9  
    10  	shimjson "github.com/anthropics/anthropic-sdk-go/internal/encoding/json"
    11  
    12  	"github.com/tidwall/sjson"
    13  )
    14  
    15  // EncodedAsDate is not be stable and shouldn't be relied upon
    16  type EncodedAsDate Opt[time.Time]
    17  
    18  // If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
    19  // special characters that sjson interprets as a path.
    20  var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace
    21  
    22  type forceOmit int
    23  
    24  func (m EncodedAsDate) MarshalJSON() ([]byte, error) {
    25  	underlying := Opt[time.Time](m)
    26  	bytes := underlying.MarshalJSONWithTimeLayout("2006-01-02")
    27  	if len(bytes) > 0 {
    28  		return bytes, nil
    29  	}
    30  	return underlying.MarshalJSON()
    31  }
    32  
    33  // MarshalObject uses a shimmed 'encoding/json' from Go 1.24, to support the 'omitzero' tag
    34  //
    35  // Stability for the API of MarshalObject is not guaranteed.
    36  func MarshalObject[T ParamStruct](f T, underlying any) ([]byte, error) {
    37  	return MarshalWithExtras(f, underlying, f.extraFields())
    38  }
    39  
    40  // MarshalWithExtras is used to marshal a struct with additional properties.
    41  //
    42  // Stability for the API of MarshalWithExtras is not guaranteed.
    43  func MarshalWithExtras[T ParamStruct, R any](f T, underlying any, extras map[string]R) ([]byte, error) {
    44  	if f.null() {
    45  		return []byte("null"), nil
    46  	} else if len(extras) > 0 {
    47  		bytes, err := shimjson.Marshal(underlying)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  		for k, v := range extras {
    52  			var a any = v
    53  			if a == Omit {
    54  				// Errors when handling ForceOmitted are ignored.
    55  				if b, e := sjson.DeleteBytes(bytes, EscapeSJSONKey(k)); e == nil {
    56  					bytes = b
    57  				}
    58  				continue
    59  			}
    60  			// Marshal the value ourselves so extras are HTML-escaped and
    61  			// validated like every other field; sjson would insert it raw.
    62  			raw, err := shimjson.Marshal(v)
    63  			if err != nil {
    64  				return nil, err
    65  			}
    66  			bytes, err = sjson.SetRawBytes(bytes, EscapeSJSONKey(k), raw)
    67  			if err != nil {
    68  				return nil, err
    69  			}
    70  		}
    71  		return bytes, nil
    72  	} else if ovr, ok := f.Overrides(); ok {
    73  		return shimjson.Marshal(ovr)
    74  	} else {
    75  		return shimjson.Marshal(underlying, shimjson.WithSkipCompaction(true))
    76  	}
    77  }
    78  
    79  // MarshalUnion uses a shimmed 'encoding/json' from Go 1.24, to support the 'omitzero' tag
    80  //
    81  // Stability for the API of MarshalUnion is not guaranteed.
    82  func MarshalUnion[T ParamStruct](metadata T, variants ...any) ([]byte, error) {
    83  	nPresent := 0
    84  	presentIdx := -1
    85  	for i, variant := range variants {
    86  		if !IsOmitted(variant) {
    87  			nPresent++
    88  			presentIdx = i
    89  		}
    90  	}
    91  	if nPresent == 0 || presentIdx == -1 {
    92  		if metadata.null() {
    93  			return []byte("null"), nil
    94  		}
    95  		if ovr, ok := metadata.Overrides(); ok {
    96  			return shimjson.Marshal(ovr)
    97  		}
    98  		return []byte(`null`), nil
    99  	} else if nPresent > 1 {
   100  		return nil, &json.MarshalerError{
   101  			Type: typeFor[T](),
   102  			Err:  fmt.Errorf("expected union to have only one present variant, got %d", nPresent),
   103  		}
   104  	}
   105  	return shimjson.Marshal(variants[presentIdx], shimjson.WithSkipCompaction(true))
   106  }
   107  
   108  // typeFor is shimmed from Go 1.23 "reflect" package
   109  func typeFor[T any]() reflect.Type {
   110  	var v T
   111  	if t := reflect.TypeOf(v); t != nil {
   112  		return t // optimize for T being a non-interface kind
   113  	}
   114  	return reflect.TypeOf((*T)(nil)).Elem() // only for an interface kind
   115  }