func (form *FormData) readFile(path, filename string, target *string) *FormData {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		form.append(
			fmt.Errorf("form file '%s' is invalid (%w)", filename, err),
		)
		return form
	}
	*target = string(b)
	return form
}
readFile binds the content of a file to a string variable. It populates an
error if it fails to read the file content.
package api
import (
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"
	"go.uber.org/multierr"
)
type FormData struct {
	values map[string][]string
	files  map[string]string
	errors error
}
func (form FormData) Validate() error {
	if form.errors == nil {
		return nil
	}
	return WrapError(
		form.errors,
		NewSentinelHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid form data: %s", form.errors)),
	)
}
func (form *FormData) String(key string, target *string, defaultValue string) *FormData {
	return form.mustValue(key, target, defaultValue)
}
func (form *FormData) MandatoryString(key string, target *string) *FormData {
	return form.mustMandatoryValue(key, target)
}
func (form *FormData) Bool(key string, target *bool, defaultValue bool) *FormData {
	return form.mustValue(key, target, defaultValue)
}
func (form *FormData) MandatoryBool(key string, target *bool) *FormData {
	return form.mustMandatoryValue(key, target)
}
func (form *FormData) Int(key string, target *int, defaultValue int) *FormData {
	return form.mustValue(key, target, defaultValue)
}
func (form *FormData) MandatoryInt(key string, target *int) *FormData {
	return form.mustMandatoryValue(key, target)
}
func (form *FormData) Float64(key string, target *float64, defaultValue float64) *FormData {
	return form.mustValue(key, target, defaultValue)
}
func (form *FormData) MandatoryFloat64(key string, target *float64) *FormData {
	return form.mustMandatoryValue(key, target)
}
func (form *FormData) Duration(key string, target *time.Duration, defaultValue time.Duration) *FormData {
	return form.mustValue(key, target, defaultValue)
}
func (form *FormData) MandatoryDuration(key string, target *time.Duration) *FormData {
	return form.mustMandatoryValue(key, target)
}
func (form *FormData) Custom(key string, assign func(value string) error) *FormData {
	var value string
	form.mustValue(key, &value, "")
	err := assign(value)
	if err != nil {
		form.append(
			fmt.Errorf("form value '%s' is invalid (got '%s', resulting to %w)", key, value, err),
		)
	}
	return form
}
func (form *FormData) MandatoryCustom(key string, assign func(value string) error) *FormData {
	var value string
	form.mustMandatoryValue(key, &value)
	if value == "" {
		return form
	}
	err := assign(value)
	if err != nil {
		form.append(
			fmt.Errorf("form value '%s' is invalid (got '%s', resulting to %w)", key, value, err),
		)
	}
	return form
}
func (form *FormData) Path(filename string, target *string) *FormData {
	return form.path(filename, target)
}
func (form *FormData) MandatoryPath(filename string, target *string) *FormData {
	return form.mandatoryPath(filename, target)
}
func (form *FormData) Content(filename string, target *string, defaultValue string) *FormData {
	var path string
	form.path(filename, &path)
	if path == "" {
		*target = defaultValue
		return form
	}
	return form.readFile(path, filename, target)
}
func (form *FormData) MandatoryContent(filename string, target *string) *FormData {
	var path string
	form.mandatoryPath(filename, &path)
	if path == "" {
		return form
	}
	return form.readFile(path, filename, target)
}
func (form *FormData) Paths(extensions []string, target *[]string) *FormData {
	return form.paths(extensions, target)
}
func (form *FormData) MandatoryPaths(extensions []string, target *[]string) *FormData {
	form.paths(extensions, target)
	if len(*target) > 0 {
		return form
	}
	form.append(
		fmt.Errorf("no form file found for extensions: %v", extensions),
	)
	return form
}
func (form *FormData) paths(extensions []string, target *[]string) *FormData {
	for filename, path := range form.files {
		for _, ext := range extensions {
			if strings.ToLower(filepath.Ext(filename)) == ext {
				*target = append(*target, path)
			}
		}
	}
	sort.Strings(*target)
	return form
}
func (form *FormData) append(err error) {
	form.errors = multierr.Append(form.errors, err)
}
func (form *FormData) mustValue(key string, target interface{}, defaultValue interface{}) *FormData {
	val, ok := form.values[key]
	if !ok || val[0] == "" {
		switch t := (target).(type) {
		case *string:
			*t = defaultValue.(string)
		case *bool:
			*t = defaultValue.(bool)
		case *int:
			*t = defaultValue.(int)
		case *float64:
			*t = defaultValue.(float64)
		case *time.Duration:
			*t = defaultValue.(time.Duration)
		default:
			panic("target type not supported")
		}
		return form
	}
	return form.mustAssign(key, val[0], target)
}
func (form *FormData) mustMandatoryValue(key string, target interface{}) *FormData {
	val, ok := form.values[key]
	if !ok || val[0] == "" {
		form.append(
			fmt.Errorf("form value '%s' is required", key),
		)
		return form
	}
	form.mustAssign(key, val[0], target)
	return form
}
func (form *FormData) mustAssign(key, value string, target interface{}) *FormData {
	var err error
	switch t := (target).(type) {
	case *string:
		*t = value
	case *bool:
		*t, err = strconv.ParseBool(value)
	case *int:
		*t, err = strconv.Atoi(value)
	case *float64:
		*t, err = strconv.ParseFloat(value, 64)
	case *time.Duration:
		*t, err = time.ParseDuration(value)
	default:
		panic("target type not supported")
	}
	if err != nil {
		form.append(
			fmt.Errorf("form value '%s' is invalid (got '%s', resulting to %w)", key, value, err),
		)
	}
	return form
}
func (form *FormData) path(filename string, target *string) *FormData {
	for name, path := range form.files {
		nameLowerExt := strings.TrimSuffix(name, filepath.Ext(name)) + strings.ToLower(filepath.Ext(name))
		if name == filename || nameLowerExt == filename {
			*target = path
			return form
		}
	}
	return form
}
func (form *FormData) mandatoryPath(filename string, target *string) *FormData {
	form.path(filename, target)
	if *target != "" {
		return form
	}
	form.append(
		fmt.Errorf("form file '%s' is required", filename),
	)
	return form
}