Files
plexgo/internal/utils/requestbody.go
speakeasybot 3ae6d27c3c ## Go SDK Changes:
* `PlexApi.LibraryPlaylists.AddPlaylistItems()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Hubs.GetMetadataHubs()`: 
  *  `request.Request.OnlyTransient` **Changed**
  *  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryCollections.MoveCollectionItem()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryCollections.DeleteCollectionItem()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryCollections.AddCollectionItems()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Content.GetSonicallySimilar()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Butler.StopTask()`:  `request.Request` **Changed** **Breaking** ⚠️
* `PlexApi.Butler.StartTask()`:  `request.Request` **Changed** **Breaking** ⚠️
* `PlexApi.Content.GetSonicPath()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.DownloadQueue.GetItemDecision()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Hubs.GetAllHubs()`: 
  *  `request.Request.OnlyTransient` **Changed**
  *  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Hubs.GetContinueWatching()`:  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Hubs.GetPromotedHubs()`:  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Content.GetAllLeaves()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Hubs.GetPostplayHubs()`: 
  *  `request.Request.OnlyTransient` **Changed**
  *  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Hubs.GetRelatedHubs()`: 
  *  `request.Request.OnlyTransient` **Changed**
  *  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Hubs.GetSectionHubs()`: 
  *  `request.Request.OnlyTransient` **Changed**
  *  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Content.ListContent()`: 
  *  `request.Request` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Content.GetAlbums()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Search.SearchHubs()`:  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Search.VoiceSearchHubs()`: 
  *  `request.Request.Type` **Changed** **Breaking** ⚠️
  *  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetLibraryItems()`: 
  *  `request.Request.MediaQuery` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.IngestTransientItem()`: 
  *  `request.Request` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetLibraryMatches()`: 
  *  `request.Request` **Changed** **Breaking** ⚠️
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Content.GetMetadataItem()`: 
  *  `request.Request` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetSections()`:  `response.MediaContainer.Directory.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.AddSection()`: 
  *  `request.Request` **Changed**
  *  `response.MediaContainer.Directory.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetTags()`: 
  *  `request.Request.Type` **Changed** **Breaking** ⚠️
* `PlexApi.Content.GetCollectionItems()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetAllItemLeaves()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Status.ListSessions()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.PlayQueue.MovePlayQueueItem()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetExtras()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.PlayQueue.DeletePlayQueueItem()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.PlayQueue.Unshuffle()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.ListMatches()`: 
  *  `request.Request.Manual` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.ListSonicallySimilar()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.PlayQueue.ResetPlayQueue()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetRelatedItems()`:  `response.MediaContainer.Hub.[].Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.ListSimilar()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.PlayQueue.ClearPlayQueue()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetItemTree()`:  `response.MediaContainer.MetadataItem.[]` **Changed** **Breaking** ⚠️
* `PlexApi.PlayQueue.AddToPlayQueue()`: 
  *  `request.Request.Next` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.PlayQueue.GetPlayQueue()`: 
  *  `request.Request` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryPlaylists.MovePlaylistItem()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetPerson()`:  `response.MediaContainer.Directory.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.ListPersonMedia()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryPlaylists.GetPlaylistGeneratorItems()`:  `response.MediaContainer.Metadata` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetLibraryDetails()`: 
  *  `request.Request.IncludeDetails` **Changed**
  *  `response.MediaContainer.Directory.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryPlaylists.ModifyPlaylistGenerator()`: 
  *  `request.Request.Item` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.Autocomplete()`: 
  *  `request.Request.MediaQuery` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetCollections()`: 
  *  `request.Request.MediaQuery` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.GetCommon()`: 
  *  `request.Request.MediaQuery` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryPlaylists.DeletePlaylistItem()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryPlaylists.ClearPlaylistItems()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.PlayQueue.Shuffle()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LibraryPlaylists.CreatePlaylist()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Playlist.GetPlaylistItems()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Playlist.GetPlaylist()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Collections.CreateCollection()`: 
  *  `request.Request.Type` **Changed** **Breaking** ⚠️
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.DvRs.TuneChannel()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LiveTv.GetSessions()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.LiveTv.GetLiveTvSession()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Playlist.ListPlaylists()`:  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Subscriptions.GetAllSubscriptions()`: 
  *  `request.Request` **Changed**
  *  `response.MediaContainer.MediaSubscription.[].MediaGrabOperation.[].Metadata` **Changed** **Breaking** ⚠️
* `PlexApi.Subscriptions.CreateSubscription()`:  `response.MediaContainer.MediaSubscription.[].MediaGrabOperation.[].Metadata` **Changed** **Breaking** ⚠️
* `PlexApi.Subscriptions.GetScheduledRecordings()`:  `response.MediaContainer.MediaGrabOperation.[].Metadata` **Changed** **Breaking** ⚠️
* `PlexApi.Subscriptions.GetTemplate()`:  `response.MediaContainer.SubscriptionTemplate.[].MediaSubscription.[].MediaGrabOperation.[].Metadata` **Changed** **Breaking** ⚠️
* `PlexApi.Subscriptions.GetSubscription()`: 
  *  `request.Request` **Changed**
  *  `response.MediaContainer.MediaSubscription.[].MediaGrabOperation.[].Metadata` **Changed** **Breaking** ⚠️
* `PlexApi.Subscriptions.EditSubscriptionPreferences()`:  `response.MediaContainer.MediaSubscription.[].MediaGrabOperation.[].Metadata` **Changed** **Breaking** ⚠️
* `PlexApi.Subscriptions.ReorderSubscription()`:  `response.MediaContainer.MediaSubscription.[].MediaGrabOperation.[].Metadata` **Changed** **Breaking** ⚠️
* `PlexApi.Transcoder.MakeDecision()`: 
  *  `request.Request` **Changed**
  *  `response.MediaContainer.Metadata.[]` **Changed** **Breaking** ⚠️
* `PlexApi.Library.RefreshItemsMetadata()`: 
  *  `request.Request.MarkUpdated` **Changed**
* `PlexApi.Authentication.Post-Users-Sign-In-Data()`: **Added**
* `PlexApi.Transcoder.StartTranscodeSession()`:  `request.Request` **Changed**
* `PlexApi.Devices.ModifyDevice()`: 
  *  `request.Request.Enabled` **Changed**
* `PlexApi.Library.GetMediaPart()`: 
  *  `request.Request.Download` **Changed**
* `PlexApi.Library.DetectIntros()`: 
  *  `request.Request.Force` **Changed**
* `PlexApi.Library.RefreshSection()`: 
  *  `request.Request.Force` **Changed**
* `PlexApi.LibraryPlaylists.UploadPlaylist()`: 
  *  `request.Request.Force` **Changed**
* `PlexApi.Library.DeleteMediaItem()`: 
  *  `request.Request.Proxy` **Changed**
* `PlexApi.Authentication.GetTokenDetails()`: **Added**
* `PlexApi.Library.GetFirstCharacters()`: 
  *  `request.Request.MediaQuery` **Changed**
* `PlexApi.Library.UpdateItems()`: 
  *  `request.Request.Field.locked` **Changed**
* `PlexApi.Library.DeleteLibrarySection()`: 
  *  `request.Request.Async` **Changed**
* `PlexApi.Library.SetStreamSelection()`: 
  *  `request.Request.AllParts` **Changed**
* `PlexApi.PlayQueue.CreatePlayQueue()`:  `request.Request` **Changed**
* `PlexApi.Library.GetAugmentationStatus()`: 
  *  `request.Request.Wait` **Changed**
* `PlexApi.Library.DetectVoiceActivity()`:  `request.Request` **Changed**
* `PlexApi.Transcoder.TranscodeImage()`:  `request.Request` **Changed**
* `PlexApi.Transcoder.TranscodeSubtitles()`:  `request.Request` **Changed**
* `PlexApi.Library.AddSubtitles()`:  `request.Request` **Changed**
* `PlexApi.Library.GetStream()`: 
  *  `request.Request.AutoAdjustSubtitle` **Changed**
* `PlexApi.Library.StartBifGeneration()`: 
  *  `request.Request.Force` **Changed**
* `PlexApi.Library.DetectCredits()`:  `request.Request` **Changed**
* `PlexApi.UltraBlur.GetImage()`: 
  *  `request.Request.Noise` **Changed**
* `PlexApi.Library.GenerateThumbs()`: 
  *  `request.Request.Force` **Changed**
* `PlexApi.Updater.ApplyUpdates()`:  `request.Request` **Changed**
* `PlexApi.Updater.CheckUpdates()`: 
  *  `request.Request.Download` **Changed**
* `PlexApi.Library.DeleteMetadataItem()`: 
  *  `request.Request.Proxy` **Changed**
* `PlexApi.Library.OptimizeDatabase()`: 
  *  `request.Request.Async` **Changed**
* `PlexApi.Hubs.UpdateHubVisibility()`:  `request.Request` **Changed**
* `PlexApi.Hubs.CreateCustomHub()`:  `request.Request` **Changed**
* `PlexApi.Library.GetSectionImage()`: 
  *  `request.Request.MediaQuery` **Changed**
* `PlexApi.DownloadQueue.AddDownloadQueueItems()`:  `request.Request` **Changed**
* `PlexApi.Timeline.Report()`:  `request.Request` **Changed**
* `PlexApi.General.GetSourceConnectionInformation()`: 
  *  `request.Request.Refresh` **Changed**
* `PlexApi.Plex.Get-Server-Resources()`: **Added**
* `PlexApi.Users.Get-Users()`: **Added**
2025-12-01 00:15:28 +00:00

459 lines
12 KiB
Go

// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
package utils
import (
"bytes"
"context"
"fmt"
"io"
"mime"
"mime/multipart"
"net/textproto"
"net/url"
"path/filepath"
"reflect"
"regexp"
"github.com/LukeHagar/plexgo/optionalnullable"
)
const (
requestTagKey = "request"
multipartFormTagKey = "multipartForm"
formTagKey = "form"
)
var (
jsonEncodingRegex = regexp.MustCompile(`(application|text)\/.*?\+*json.*`)
multipartEncodingRegex = regexp.MustCompile(`multipart\/.*`)
urlEncodedEncodingRegex = regexp.MustCompile(`application\/x-www-form-urlencoded.*`)
)
func SerializeRequestBody(_ context.Context, request interface{}, nullable, optional bool, requestFieldName, serializationMethod, tag string) (io.Reader, string, error) {
bodyReader, contentType, err := serializeRequestBody(request, nullable, optional, requestFieldName, serializationMethod, tag)
if err != nil {
return nil, "", fmt.Errorf("error serializing request body: %w", err)
}
if bodyReader == nil && !optional {
return nil, "", fmt.Errorf("request body is required")
}
return bodyReader, contentType, nil
}
func serializeRequestBody(request interface{}, nullable, optional bool, requestFieldName, serializationMethod, tag string) (io.Reader, string, error) {
requestStructType := reflect.TypeOf(request)
requestValType := reflect.ValueOf(request)
if isNil(requestStructType, requestValType) {
if !nullable && optional {
return nil, "", nil
}
return serializeContentType(requestFieldName, SerializationMethodToContentType[serializationMethod], requestValType, tag)
}
if requestStructType.Kind() == reflect.Pointer {
requestStructType = requestStructType.Elem()
requestValType = requestValType.Elem()
}
if requestStructType.Kind() != reflect.Struct {
return serializeContentType(requestFieldName, SerializationMethodToContentType[serializationMethod], requestValType, tag)
}
requestField, ok := requestStructType.FieldByName(requestFieldName)
if ok {
tag := getRequestTag(requestField)
if tag != nil {
// request object (non-flattened)
requestVal := requestValType.FieldByName(requestFieldName)
val := reflect.ValueOf(requestVal.Interface())
if isNil(requestField.Type, requestVal) {
if !nullable && optional {
return nil, "", nil
}
return serializeContentType(requestFieldName, tag.MediaType, val, string(requestField.Tag))
}
return serializeContentType(requestFieldName, tag.MediaType, val, string(requestField.Tag))
}
}
// flattened request object
return serializeContentType(requestFieldName, SerializationMethodToContentType[serializationMethod], reflect.ValueOf(request), tag)
}
func serializeContentType(fieldName string, mediaType string, val reflect.Value, tag string) (io.Reader, string, error) {
buf := &bytes.Buffer{}
if isNil(val.Type(), val) {
// TODO: what does a null mean for other content types? Just returning an empty buffer for now
if jsonEncodingRegex.MatchString(mediaType) {
if _, err := buf.Write([]byte("null")); err != nil {
return nil, "", err
}
}
return buf, mediaType, nil
}
switch {
case jsonEncodingRegex.MatchString(mediaType):
data, err := MarshalJSON(val.Interface(), reflect.StructTag(tag), true)
if err != nil {
return nil, "", err
}
if _, err := buf.Write(data); err != nil {
return nil, "", err
}
case multipartEncodingRegex.MatchString(mediaType):
var err error
mediaType, err = encodeMultipartFormData(buf, val.Interface())
if err != nil {
return nil, "", err
}
case urlEncodedEncodingRegex.MatchString(mediaType):
if err := encodeFormData(fieldName, buf, val.Interface()); err != nil {
return nil, "", err
}
case val.Type().Implements(reflect.TypeOf((*io.Reader)(nil)).Elem()):
return val.Interface().(io.Reader), mediaType, nil
default:
val = reflect.Indirect(val)
switch {
case val.Type().Kind() == reflect.String:
if _, err := buf.WriteString(valToString(val.Interface())); err != nil {
return nil, "", err
}
case reflect.TypeOf(val.Interface()) == reflect.TypeOf([]byte(nil)):
if _, err := buf.Write(val.Interface().([]byte)); err != nil {
return nil, "", err
}
default:
return nil, "", fmt.Errorf("invalid request body type %s for mediaType %s", val.Type(), mediaType)
}
}
return buf, mediaType, nil
}
func encodeMultipartFormData(w io.Writer, data interface{}) (string, error) {
requestStructType := reflect.TypeOf(data)
requestValType := reflect.ValueOf(data)
if requestStructType.Kind() == reflect.Pointer {
requestStructType = requestStructType.Elem()
requestValType = requestValType.Elem()
}
writer := multipart.NewWriter(w)
for i := 0; i < requestStructType.NumField(); i++ {
field := requestStructType.Field(i)
fieldType := field.Type
valType := requestValType.Field(i)
if isNil(fieldType, valType) {
continue
}
if fieldType.Kind() == reflect.Pointer {
fieldType = fieldType.Elem()
valType = valType.Elem()
}
tag := parseMultipartFormTag(field)
if tag.File {
switch fieldType.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < valType.Len(); i++ {
arrayVal := valType.Index(i)
if err := encodeMultipartFormDataFile(writer, tag.Name+"[]", arrayVal.Type(), arrayVal); err != nil {
writer.Close()
return "", err
}
}
default:
if err := encodeMultipartFormDataFile(writer, tag.Name, fieldType, valType); err != nil {
writer.Close()
return "", err
}
}
} else if tag.JSON {
jw, err := writer.CreateFormField(tag.Name)
if err != nil {
writer.Close()
return "", err
}
d, err := MarshalJSON(valType.Interface(), field.Tag, true)
if err != nil {
writer.Close()
return "", err
}
if _, err := jw.Write(d); err != nil {
writer.Close()
return "", err
}
} else {
switch fieldType.Kind() {
case reflect.Slice, reflect.Array:
values := parseDelimitedArray(true, valType, ",")
for _, v := range values {
if err := writer.WriteField(tag.Name, v); err != nil {
writer.Close()
return "", err
}
}
default:
if err := writer.WriteField(tag.Name, valToString(valType.Interface())); err != nil {
writer.Close()
return "", err
}
}
}
}
if err := writer.Close(); err != nil {
return "", err
}
return writer.FormDataContentType(), nil
}
func encodeMultipartFormDataFile(w *multipart.Writer, fieldName string, fieldType reflect.Type, valType reflect.Value) error {
if fieldType.Kind() != reflect.Struct {
return fmt.Errorf("invalid type %s for multipart/form-data file", valType.Type())
}
var fileName string
var reader io.Reader
for i := 0; i < fieldType.NumField(); i++ {
field := fieldType.Field(i)
val := valType.Field(i)
tag := parseMultipartFormTag(field)
if !tag.Content && tag.Name == "" {
continue
}
if tag.Content && val.CanInterface() {
if reflect.TypeOf(val.Interface()) == reflect.TypeOf([]byte(nil)) {
reader = bytes.NewReader(val.Interface().([]byte))
} else if reflect.TypeOf(val.Interface()).Implements(reflect.TypeOf((*io.Reader)(nil)).Elem()) {
reader = val.Interface().(io.Reader)
}
} else {
fileName = val.String()
}
}
if fileName == "" || reader == nil {
return fmt.Errorf("invalid multipart/form-data file")
}
// Detect content type based on file extension
contentType := mime.TypeByExtension(filepath.Ext(fileName))
if contentType == "" {
contentType = "application/octet-stream"
}
// Create multipart header with proper content type
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, fieldName, fileName))
h.Set("Content-Type", contentType)
fw, err := w.CreatePart(h)
if err != nil {
return err
}
if _, err := io.Copy(fw, reader); err != nil {
return err
}
// Reset seek position to 0 if the reader supports seeking
if seeker, ok := reader.(io.Seeker); ok {
_, _ = seeker.Seek(0, io.SeekStart)
}
return nil
}
func encodeFormData(fieldName string, w io.Writer, data interface{}) error {
requestType := reflect.TypeOf(data)
requestValType := reflect.ValueOf(data)
if requestType.Kind() == reflect.Pointer {
requestType = requestType.Elem()
requestValType = requestValType.Elem()
}
dataValues := url.Values{}
switch requestType.Kind() {
case reflect.Struct:
for i := 0; i < requestType.NumField(); i++ {
field := requestType.Field(i)
fieldType := field.Type
valType := requestValType.Field(i)
if isNil(fieldType, valType) {
continue
}
if fieldType.Kind() == reflect.Pointer {
fieldType = fieldType.Elem()
valType = valType.Elem()
}
tag := parseFormTag(field)
if tag.JSON {
data, err := MarshalJSON(valType.Interface(), field.Tag, true)
if err != nil {
return err
}
dataValues.Set(tag.Name, string(data))
} else {
switch tag.Style {
// TODO: support other styles
case "form":
values := populateForm(tag.Name, tag.Explode, fieldType, valType, ",", nil, nil, func(sf reflect.StructField) string {
tag := parseFormTag(field)
if tag == nil {
return ""
}
return tag.Name
})
for k, v := range values {
for _, vv := range v {
dataValues.Add(k, vv)
}
}
}
}
}
case reflect.Map:
// check if optionalnullable.OptionalNullable[T]
if nullableValue, ok := optionalnullable.AsOptionalNullable(requestValType); ok {
// Handle optionalnullable.OptionalNullable[T] using GetUntyped method
if value, isSet := nullableValue.GetUntyped(); isSet && value != nil {
dataValues.Set(fieldName, valToString(value))
}
// If not set or explicitly null, skip adding to form
break
}
// Handle regular map
for _, k := range requestValType.MapKeys() {
v := requestValType.MapIndex(k)
dataValues.Set(fmt.Sprintf("%v", k.Interface()), valToString(v.Interface()))
}
case reflect.Slice, reflect.Array:
for i := 0; i < requestValType.Len(); i++ {
v := requestValType.Index(i)
dataValues.Set(fieldName, valToString(v.Interface()))
}
}
if _, err := w.Write([]byte(dataValues.Encode())); err != nil {
return err
}
return nil
}
type requestTag struct {
MediaType string
}
func getRequestTag(field reflect.StructField) *requestTag {
// example `request:"mediaType=multipart/form-data"`
values := parseStructTag(requestTagKey, field)
if values == nil {
return nil
}
tag := &requestTag{
MediaType: "application/octet-stream",
}
for k, v := range values {
switch k {
case "mediaType":
tag.MediaType = v
}
}
return tag
}
type multipartFormTag struct {
File bool
Content bool
JSON bool
Name string
}
func parseMultipartFormTag(field reflect.StructField) *multipartFormTag {
// example `multipartForm:"name=file"`
values := parseStructTag(multipartFormTagKey, field)
tag := &multipartFormTag{}
for k, v := range values {
switch k {
case "file":
tag.File = v == "true"
case "content":
tag.Content = v == "true"
case "name":
tag.Name = v
case "json":
tag.JSON = v == "true"
}
}
return tag
}
type formTag struct {
Name string
JSON bool
Style string
Explode bool
}
func parseFormTag(field reflect.StructField) *formTag {
// example `form:"name=propName,style=spaceDelimited,explode"`
values := parseStructTag(formTagKey, field)
tag := &formTag{
Style: "form",
Explode: true,
}
for k, v := range values {
switch k {
case "name":
tag.Name = v
case "json":
tag.JSON = v == "true"
case "style":
tag.Style = v
case "explode":
tag.Explode = v == "true"
}
}
return tag
}