summaryrefslogtreecommitdiff
path: root/teleirc/matterbridge/vendor/github.com/labstack/echo/v4/bind.go
diff options
context:
space:
mode:
authorMistivia <i@mistivia.com>2025-11-02 15:27:18 +0800
committerMistivia <i@mistivia.com>2025-11-02 15:27:18 +0800
commite9c24f4af7ed56760f6db7941827d09f6db9020b (patch)
tree62128c43b883ce5e3148113350978755779bb5de /teleirc/matterbridge/vendor/github.com/labstack/echo/v4/bind.go
parent58d5e7cfda4781d8a57ec52aefd02983835c301a (diff)
add matterbridge
Diffstat (limited to 'teleirc/matterbridge/vendor/github.com/labstack/echo/v4/bind.go')
-rw-r--r--teleirc/matterbridge/vendor/github.com/labstack/echo/v4/bind.go340
1 files changed, 340 insertions, 0 deletions
diff --git a/teleirc/matterbridge/vendor/github.com/labstack/echo/v4/bind.go b/teleirc/matterbridge/vendor/github.com/labstack/echo/v4/bind.go
new file mode 100644
index 0000000..c841ca0
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/labstack/echo/v4/bind.go
@@ -0,0 +1,340 @@
+package echo
+
+import (
+ "encoding"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "net/http"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+type (
+ // Binder is the interface that wraps the Bind method.
+ Binder interface {
+ Bind(i interface{}, c Context) error
+ }
+
+ // DefaultBinder is the default implementation of the Binder interface.
+ DefaultBinder struct{}
+
+ // BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
+ // Types that don't implement this, but do implement encoding.TextUnmarshaler
+ // will use that interface instead.
+ BindUnmarshaler interface {
+ // UnmarshalParam decodes and assigns a value from an form or query param.
+ UnmarshalParam(param string) error
+ }
+)
+
+// BindPathParams binds path params to bindable object
+func (b *DefaultBinder) BindPathParams(c Context, i interface{}) error {
+ names := c.ParamNames()
+ values := c.ParamValues()
+ params := map[string][]string{}
+ for i, name := range names {
+ params[name] = []string{values[i]}
+ }
+ if err := b.bindData(i, params, "param"); err != nil {
+ return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
+ }
+ return nil
+}
+
+// BindQueryParams binds query params to bindable object
+func (b *DefaultBinder) BindQueryParams(c Context, i interface{}) error {
+ if err := b.bindData(i, c.QueryParams(), "query"); err != nil {
+ return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
+ }
+ return nil
+}
+
+// BindBody binds request body contents to bindable object
+// NB: then binding forms take note that this implementation uses standard library form parsing
+// which parses form data from BOTH URL and BODY if content type is not MIMEMultipartForm
+// See non-MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseForm
+// See MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseMultipartForm
+func (b *DefaultBinder) BindBody(c Context, i interface{}) (err error) {
+ req := c.Request()
+ if req.ContentLength == 0 {
+ return
+ }
+
+ ctype := req.Header.Get(HeaderContentType)
+ switch {
+ case strings.HasPrefix(ctype, MIMEApplicationJSON):
+ if err = c.Echo().JSONSerializer.Deserialize(c, i); err != nil {
+ switch err.(type) {
+ case *HTTPError:
+ return err
+ default:
+ return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
+ }
+ }
+ case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML):
+ if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
+ if ute, ok := err.(*xml.UnsupportedTypeError); ok {
+ return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())).SetInternal(err)
+ } else if se, ok := err.(*xml.SyntaxError); ok {
+ return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error())).SetInternal(err)
+ }
+ return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
+ }
+ case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm):
+ params, err := c.FormParams()
+ if err != nil {
+ return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
+ }
+ if err = b.bindData(i, params, "form"); err != nil {
+ return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
+ }
+ default:
+ return ErrUnsupportedMediaType
+ }
+ return nil
+}
+
+// BindHeaders binds HTTP headers to a bindable object
+func (b *DefaultBinder) BindHeaders(c Context, i interface{}) error {
+ if err := b.bindData(i, c.Request().Header, "header"); err != nil {
+ return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
+ }
+ return nil
+}
+
+// Bind implements the `Binder#Bind` function.
+// Binding is done in following order: 1) path params; 2) query params; 3) request body. Each step COULD override previous
+// step binded values. For single source binding use their own methods BindBody, BindQueryParams, BindPathParams.
+func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
+ if err := b.BindPathParams(c, i); err != nil {
+ return err
+ }
+ // Only bind query parameters for GET/DELETE/HEAD to avoid unexpected behavior with destination struct binding from body.
+ // For example a request URL `&id=1&lang=en` with body `{"id":100,"lang":"de"}` would lead to precedence issues.
+ // The HTTP method check restores pre-v4.1.11 behavior to avoid these problems (see issue #1670)
+ method := c.Request().Method
+ if method == http.MethodGet || method == http.MethodDelete || method == http.MethodHead {
+ if err = b.BindQueryParams(c, i); err != nil {
+ return err
+ }
+ }
+ return b.BindBody(c, i)
+}
+
+// bindData will bind data ONLY fields in destination struct that have EXPLICIT tag
+func (b *DefaultBinder) bindData(destination interface{}, data map[string][]string, tag string) error {
+ if destination == nil || len(data) == 0 {
+ return nil
+ }
+ typ := reflect.TypeOf(destination).Elem()
+ val := reflect.ValueOf(destination).Elem()
+
+ // Map
+ if typ.Kind() == reflect.Map {
+ for k, v := range data {
+ val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0]))
+ }
+ return nil
+ }
+
+ // !struct
+ if typ.Kind() != reflect.Struct {
+ if tag == "param" || tag == "query" || tag == "header" {
+ // incompatible type, data is probably to be found in the body
+ return nil
+ }
+ return errors.New("binding element must be a struct")
+ }
+
+ for i := 0; i < typ.NumField(); i++ {
+ typeField := typ.Field(i)
+ structField := val.Field(i)
+ if typeField.Anonymous {
+ if structField.Kind() == reflect.Ptr {
+ structField = structField.Elem()
+ }
+ }
+ if !structField.CanSet() {
+ continue
+ }
+ structFieldKind := structField.Kind()
+ inputFieldName := typeField.Tag.Get(tag)
+ if typeField.Anonymous && structField.Kind() == reflect.Struct && inputFieldName != "" {
+ // if anonymous struct with query/param/form tags, report an error
+ return errors.New("query/param/form tags are not allowed with anonymous struct field")
+ }
+
+ if inputFieldName == "" {
+ // If tag is nil, we inspect if the field is a not BindUnmarshaler struct and try to bind data into it (might contains fields with tags).
+ // structs that implement BindUnmarshaler are binded only when they have explicit tag
+ if _, ok := structField.Addr().Interface().(BindUnmarshaler); !ok && structFieldKind == reflect.Struct {
+ if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil {
+ return err
+ }
+ }
+ // does not have explicit tag and is not an ordinary struct - so move to next field
+ continue
+ }
+
+ inputValue, exists := data[inputFieldName]
+ if !exists {
+ // Go json.Unmarshal supports case insensitive binding. However the
+ // url params are bound case sensitive which is inconsistent. To
+ // fix this we must check all of the map values in a
+ // case-insensitive search.
+ for k, v := range data {
+ if strings.EqualFold(k, inputFieldName) {
+ inputValue = v
+ exists = true
+ break
+ }
+ }
+ }
+
+ if !exists {
+ continue
+ }
+
+ // Call this first, in case we're dealing with an alias to an array type
+ if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok {
+ if err != nil {
+ return err
+ }
+ continue
+ }
+
+ numElems := len(inputValue)
+ if structFieldKind == reflect.Slice && numElems > 0 {
+ sliceOf := structField.Type().Elem().Kind()
+ slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
+ for j := 0; j < numElems; j++ {
+ if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
+ return err
+ }
+ }
+ val.Field(i).Set(slice)
+ } else if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
+ return err
+
+ }
+ }
+ return nil
+}
+
+func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
+ // But also call it here, in case we're dealing with an array of BindUnmarshalers
+ if ok, err := unmarshalField(valueKind, val, structField); ok {
+ return err
+ }
+
+ switch valueKind {
+ case reflect.Ptr:
+ return setWithProperType(structField.Elem().Kind(), val, structField.Elem())
+ case reflect.Int:
+ return setIntField(val, 0, structField)
+ case reflect.Int8:
+ return setIntField(val, 8, structField)
+ case reflect.Int16:
+ return setIntField(val, 16, structField)
+ case reflect.Int32:
+ return setIntField(val, 32, structField)
+ case reflect.Int64:
+ return setIntField(val, 64, structField)
+ case reflect.Uint:
+ return setUintField(val, 0, structField)
+ case reflect.Uint8:
+ return setUintField(val, 8, structField)
+ case reflect.Uint16:
+ return setUintField(val, 16, structField)
+ case reflect.Uint32:
+ return setUintField(val, 32, structField)
+ case reflect.Uint64:
+ return setUintField(val, 64, structField)
+ case reflect.Bool:
+ return setBoolField(val, structField)
+ case reflect.Float32:
+ return setFloatField(val, 32, structField)
+ case reflect.Float64:
+ return setFloatField(val, 64, structField)
+ case reflect.String:
+ structField.SetString(val)
+ default:
+ return errors.New("unknown type")
+ }
+ return nil
+}
+
+func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
+ switch valueKind {
+ case reflect.Ptr:
+ return unmarshalFieldPtr(val, field)
+ default:
+ return unmarshalFieldNonPtr(val, field)
+ }
+}
+
+func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) {
+ fieldIValue := field.Addr().Interface()
+ if unmarshaler, ok := fieldIValue.(BindUnmarshaler); ok {
+ return true, unmarshaler.UnmarshalParam(value)
+ }
+ if unmarshaler, ok := fieldIValue.(encoding.TextUnmarshaler); ok {
+ return true, unmarshaler.UnmarshalText([]byte(value))
+ }
+
+ return false, nil
+}
+
+func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) {
+ if field.IsNil() {
+ // Initialize the pointer to a nil value
+ field.Set(reflect.New(field.Type().Elem()))
+ }
+ return unmarshalFieldNonPtr(value, field.Elem())
+}
+
+func setIntField(value string, bitSize int, field reflect.Value) error {
+ if value == "" {
+ value = "0"
+ }
+ intVal, err := strconv.ParseInt(value, 10, bitSize)
+ if err == nil {
+ field.SetInt(intVal)
+ }
+ return err
+}
+
+func setUintField(value string, bitSize int, field reflect.Value) error {
+ if value == "" {
+ value = "0"
+ }
+ uintVal, err := strconv.ParseUint(value, 10, bitSize)
+ if err == nil {
+ field.SetUint(uintVal)
+ }
+ return err
+}
+
+func setBoolField(value string, field reflect.Value) error {
+ if value == "" {
+ value = "false"
+ }
+ boolVal, err := strconv.ParseBool(value)
+ if err == nil {
+ field.SetBool(boolVal)
+ }
+ return err
+}
+
+func setFloatField(value string, bitSize int, field reflect.Value) error {
+ if value == "" {
+ value = "0.0"
+ }
+ floatVal, err := strconv.ParseFloat(value, bitSize)
+ if err == nil {
+ field.SetFloat(floatVal)
+ }
+ return err
+}