package sqlx import ( "fmt" "reflect" "strings" "github.com/kataras/iris/v12/x/reflex" ) // DefaultTag is the default struct field tag. var DefaultTag = "db" // ColumnNameFunc is the function which converts a struct field name to a database column name. 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 }