package jsvm import ( "bytes" "io" "mime/multipart" "github.com/pocketbase/pocketbase/tools/filesystem" "github.com/spf13/cast" ) // FormData represents an interface similar to the browser's [FormData]. // // The value of each FormData entry must be a string or [*filesystem.File] instance. // // It is intended to be used together with the JSVM `$http.send` when // sending multipart/form-data requests. // // [FormData]: https://developer.mozilla.org/en-US/docs/Web/API/FormData. type FormData map[string][]any // Append appends a new value onto an existing key inside the current FormData, // or adds the key if it does not already exist. func (data FormData) Append(key string, value any) { data[key] = append(data[key], value) } // Set sets a new value for an existing key inside the current FormData, // or adds the key/value if it does not already exist. func (data FormData) Set(key string, value any) { data[key] = []any{value} } // Delete deletes a key and its value(s) from the current FormData. func (data FormData) Delete(key string) { delete(data, key) } // Get returns the first value associated with a given key from // within the current FormData. // // If you expect multiple values and want all of them, // use the [FormData.GetAll] method instead. func (data FormData) Get(key string) any { values, ok := data[key] if !ok || len(values) == 0 { return nil } return values[0] } // GetAll returns all the values associated with a given key // from within the current FormData. func (data FormData) GetAll(key string) []any { values, ok := data[key] if !ok { return nil } return values } // Has returns whether a FormData object contains a certain key. func (data FormData) Has(key string) bool { values, ok := data[key] return ok && len(values) > 0 } // Keys returns all keys contained in the current FormData. func (data FormData) Keys() []string { result := make([]string, 0, len(data)) for k := range data { result = append(result, k) } return result } // Keys returns all values contained in the current FormData. func (data FormData) Values() []any { result := make([]any, 0, len(data)) for _, values := range data { for _, v := range values { result = append(result, v) } } return result } // Entries returns a [key, value] slice pair for each FormData entry. func (data FormData) Entries() [][]any { result := make([][]any, 0, len(data)) for k, values := range data { for _, v := range values { result = append(result, []any{k, v}) } } return result } // toMultipart converts the current FormData entries into multipart encoded data. func (data FormData) toMultipart() (*bytes.Buffer, *multipart.Writer, error) { body := new(bytes.Buffer) mp := multipart.NewWriter(body) defer mp.Close() for k, values := range data { for _, rawValue := range values { switch v := rawValue.(type) { case *filesystem.File: err := func() error { mpw, err := mp.CreateFormFile(k, v.OriginalName) if err != nil { return err } file, err := v.Reader.Open() if err != nil { return err } defer file.Close() if _, err := io.Copy(mpw, file); err != nil { return err } return nil }() if err != nil { return nil, nil, err } default: if err := mp.WriteField(k, cast.ToString(v)); err != nil { return nil, nil, err } } } } return body, mp, nil }