2022-03-07 23:33:08 +01:00
|
|
|
package sqlx
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/kataras/iris/v12/x/reflex"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DefaultTag is the default struct field tag.
|
|
|
|
var DefaultTag = "db"
|
|
|
|
|
2023-11-05 21:20:20 +01:00
|
|
|
// ColumnNameFunc is the function which converts a struct field name to a database column name.
|
2022-03-07 23:33:08 +01:00
|
|
|
type ColumnNameFunc = func(string) string
|
|
|
|
|
|
|
|
func convertStructToColumns(typ reflect.Type, nameFunc ColumnNameFunc) (map[string]*Column, error) {
|
|
|
|
if kind := typ.Kind(); kind != reflect.Struct {
|
|
|
|
return nil, fmt.Errorf("convert struct: invalid type: expected a struct value but got: %q", kind.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve only fields valid for database.
|
|
|
|
fields := reflex.LookupFields(typ, "")
|
|
|
|
|
|
|
|
columns := make(map[string]*Column, len(fields))
|
|
|
|
for i, field := range fields {
|
|
|
|
column, ok, err := convertStructFieldToColumn(field, DefaultTag, nameFunc)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("convert struct: field name: %q: %w", field.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
column.Index = i
|
|
|
|
columns[column.Name] = column
|
|
|
|
}
|
|
|
|
|
|
|
|
return columns, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertStructFieldToColumn(field reflect.StructField, optionalTag string, nameFunc ColumnNameFunc) (*Column, bool, error) {
|
|
|
|
c := &Column{
|
|
|
|
Name: nameFunc(field.Name),
|
|
|
|
FieldIndex: field.Index,
|
|
|
|
}
|
|
|
|
|
|
|
|
fieldTag, ok := field.Tag.Lookup(optionalTag)
|
|
|
|
if ok {
|
|
|
|
if fieldTag == "-" {
|
|
|
|
return nil, false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := parseOptions(fieldTag, c); err != nil {
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return c, true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseOptions(fieldTag string, c *Column) error {
|
|
|
|
options := strings.Split(fieldTag, ",")
|
|
|
|
for _, opt := range options {
|
|
|
|
if opt == "" {
|
|
|
|
continue // skip empty.
|
|
|
|
}
|
|
|
|
|
|
|
|
var key, value string
|
|
|
|
|
|
|
|
kv := strings.Split(opt, "=") // When more options come to play.
|
|
|
|
switch len(kv) {
|
|
|
|
case 2:
|
|
|
|
key = kv[0]
|
|
|
|
value = kv[1]
|
|
|
|
case 1:
|
|
|
|
c.Name = kv[0]
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("option: %s: expected key value separated by '='", opt)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch key {
|
|
|
|
case "name":
|
|
|
|
c.Name = value
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unexpected tag option: %s", key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|