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 }