diamond-orm/field.go

134 lines
2.9 KiB
Go

package orm
import (
"fmt"
"net"
"reflect"
"time"
)
type Field struct {
Name string
ColumnName string
ColumnType string
Type reflect.Type
Original reflect.StructField
Model *Model
Index int
AutoIncrement bool
PrimaryKey bool
Nullable bool
isForeignKey bool
fk *Relationship
}
func (f *Field) alias() (string, string) {
columnName := f.Model.Fields[f.Model.IDField].ColumnName
if f.ColumnType != "" {
columnName = f.ColumnName
}
return fmt.Sprintf("%s.%s", f.Model.TableName, columnName), fmt.Sprintf("%s_%s", f.Model.TableName, f.ColumnName)
}
func (f *Field) aliasWith(a string) (string, string) {
first := fmt.Sprintf("%s.%s", a, f.ColumnName)
second := fmt.Sprintf("%s_%s", a, f.ColumnName)
return first, second
}
func (f *Field) key() string {
return fmt.Sprintf("%s.%s", f.Model.Name, f.Name)
}
func columnType(ty reflect.Type, isPk, isAutoInc bool) string {
it := ty
switch it.Kind() {
case reflect.Ptr:
for it.Kind() == reflect.Ptr {
it = it.Elem()
}
case reflect.Int32, reflect.Uint32:
if isPk || isAutoInc {
return "serial"
} else {
return "int"
}
case reflect.Int64, reflect.Uint64, reflect.Int, reflect.Uint:
if isPk || isAutoInc {
return "bigserial"
} else {
return "bigint"
}
case reflect.String:
return "text"
case reflect.Float32:
return "float4"
case reflect.Float64:
return "double precision"
case reflect.Bool:
return "boolean"
case reflect.Struct:
if canConvertTo[time.Time](ty) {
return "timestamptz"
}
if canConvertTo[net.IP](ty) {
return "inet"
}
if canConvertTo[net.IPNet](ty) {
return "cidr"
}
default:
return ""
}
return ""
}
func parseField(f reflect.StructField, minfo *Model, modelMap map[string]*Model, i int) *Field {
field := &Field{
Name: f.Name,
Original: f,
Index: i,
}
tags := parseTags(f.Tag.Get("d"))
if tags["-"] != "" {
return nil
}
field.PrimaryKey = tags["pk"] != "" || tags["primarykey"] != "" || field.Name == "ID"
field.AutoIncrement = tags["autoinc"] != ""
field.Nullable = tags["nullable"] != ""
field.ColumnType = tags["type"]
if field.ColumnType == "" {
field.ColumnType = columnType(f.Type, field.PrimaryKey, field.AutoIncrement)
}
field.ColumnName = tags["column"]
if field.ColumnName == "" {
field.ColumnName = pascalToSnakeCase(field.Name)
}
if field.PrimaryKey {
minfo.IDField = field.Name
}
elem := f.Type
for elem.Kind() == reflect.Ptr {
if !field.Nullable {
field.Nullable = true
}
elem = elem.Elem()
}
field.Type = elem
switch elem.Kind() {
case reflect.Array, reflect.Slice:
elem = elem.Elem()
fallthrough
case reflect.Struct:
if canConvertTo[Document](elem) && f.Anonymous {
minfo.TableName = tags["table"]
return nil
} else if field.ColumnType == "" {
minfo.Relationships[field.Name] = parseRelationship(f, modelMap, minfo.Type, i)
}
}
return field
}