1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
|
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package field
import (
"fmt"
"reflect"
"strings"
)
// A Type represents a field type.
type Type uint8
// List of field types.
const (
TypeInvalid Type = iota
TypeBool
TypeTime
TypeJSON
TypeUUID
TypeBytes
TypeEnum
TypeString
TypeOther
TypeInt8
TypeInt16
TypeInt32
TypeInt
TypeInt64
TypeUint8
TypeUint16
TypeUint32
TypeUint
TypeUint64
TypeFloat32
TypeFloat64
endTypes
)
// String returns the string representation of a type.
func (t Type) String() string {
if t < endTypes {
return typeNames[t]
}
return typeNames[TypeInvalid]
}
// Numeric reports if the given type is a numeric type.
func (t Type) Numeric() bool {
return t >= TypeInt8 && t < endTypes
}
// Float reports if the given type is a float type.
func (t Type) Float() bool {
return t == TypeFloat32 || t == TypeFloat64
}
// Integer reports if the given type is an integral type.
func (t Type) Integer() bool {
return t.Numeric() && !t.Float()
}
// Valid reports if the given type if known type.
func (t Type) Valid() bool {
return t > TypeInvalid && t < endTypes
}
// ConstName returns the constant name of a info type.
// It's used by entc for printing the constant name in templates.
func (t Type) ConstName() string {
switch {
case !t.Valid():
return typeNames[TypeInvalid]
case int(t) < len(constNames) && constNames[t] != "":
return constNames[t]
default:
return "Type" + strings.Title(typeNames[t])
}
}
// TypeInfo holds the information regarding field type.
// Used by complex types like JSON and Bytes.
type TypeInfo struct {
Type Type
Ident string
PkgPath string
Nillable bool // slices or pointers.
RType *RType
}
// String returns the string representation of a type.
func (t TypeInfo) String() string {
switch {
case t.Ident != "":
return t.Ident
case t.Type < endTypes:
return typeNames[t.Type]
default:
return typeNames[TypeInvalid]
}
}
// Valid reports if the given type if known type.
func (t TypeInfo) Valid() bool {
return t.Type.Valid()
}
// Numeric reports if the given type is a numeric type.
func (t TypeInfo) Numeric() bool {
return t.Type.Numeric()
}
// ConstName returns the const name of the info type.
func (t TypeInfo) ConstName() string {
return t.Type.ConstName()
}
// ValueScanner indicates if this type implements the ValueScanner interface.
func (t TypeInfo) ValueScanner() bool {
return t.RType.implements(valueScannerType)
}
// Comparable reports whether values of this type are comparable.
func (t TypeInfo) Comparable() bool {
switch t.Type {
case TypeBool, TypeTime, TypeUUID, TypeEnum, TypeString:
return true
default:
return t.Numeric()
}
}
var stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
// Stringer indicates if this type implements the Stringer interface.
func (t TypeInfo) Stringer() bool {
return t.RType.implements(stringerType)
}
var (
typeNames = [...]string{
TypeInvalid: "invalid",
TypeBool: "bool",
TypeTime: "time.Time",
TypeJSON: "json.RawMessage",
TypeUUID: "[16]byte",
TypeBytes: "[]byte",
TypeEnum: "string",
TypeString: "string",
TypeInt: "int",
TypeInt8: "int8",
TypeInt16: "int16",
TypeInt32: "int32",
TypeInt64: "int64",
TypeUint: "uint",
TypeUint8: "uint8",
TypeUint16: "uint16",
TypeUint32: "uint32",
TypeUint64: "uint64",
TypeFloat32: "float32",
TypeFloat64: "float64",
}
constNames = [...]string{
TypeJSON: "TypeJSON",
TypeUUID: "TypeUUID",
TypeTime: "TypeTime",
TypeEnum: "TypeEnum",
TypeBytes: "TypeBytes",
TypeOther: "TypeOther",
}
)
// RType holds a serializable reflect.Type information of
// Go object. Used by the entc package.
type RType struct {
Name string
Kind reflect.Kind
PkgPath string
Methods map[string]struct{ In, Out []*RType }
// Used only for in-package checks.
rtype reflect.Type
}
// TypeEqual tests if the RType is equal to given reflect.Type.
func (r *RType) TypeEqual(t reflect.Type) bool {
t = indirect(t)
return r.Name == t.Name() && r.Kind == t.Kind() && r.PkgPath == t.PkgPath()
}
func (r *RType) implements(typ reflect.Type) bool {
if r == nil {
return false
}
n := typ.NumMethod()
for i := 0; i < n; i++ {
m0 := typ.Method(i)
m1, ok := r.Methods[m0.Name]
if !ok || len(m1.In) != m0.Type.NumIn() || len(m1.Out) != m0.Type.NumOut() {
return false
}
in := m0.Type.NumIn()
for j := 0; j < in; j++ {
if !m1.In[j].TypeEqual(m0.Type.In(j)) {
return false
}
}
out := m0.Type.NumOut()
for j := 0; j < out; j++ {
if !m1.Out[j].TypeEqual(m0.Type.Out(j)) {
return false
}
}
}
return true
}
|