schema/field: allow non-string ValueScanner types for enum fields (#1577)

* Make non-string ValueScanner types work with enum fields

This change fixes #1575 which now makes it possible to specify a GoType
for an enum that is not a string alias. It requires that if the specified
type is not a string alias, the type must satisfy the Stringer interface.

* remove default, rename field

* wip

* remove comment

* go generate

* make optional

* generate 🤦

* handle NULL case

* turns out mysql stores strings as []uint8
This commit is contained in:
Andy Day
2021-05-20 13:29:47 -07:00
committed by GitHub
parent da20bba97d
commit 237799dd2e
23 changed files with 628 additions and 12 deletions

View File

@@ -808,6 +808,12 @@ type EnumValues interface {
func (b *enumBuilder) GoType(ev EnumValues) *enumBuilder {
b.Values(ev.Values()...)
b.desc.goType(ev, stringType)
// If an error already exists, let that be returned instead.
// Otherwise check that the underlying type is either a string
// or implements Stringer.
if b.desc.Err == nil && b.desc.Info.RType.rtype.Kind() != reflect.String && !b.desc.Info.Stringer() {
b.desc.Err = errors.New("enum values which implement ValueScanner must also implement Stringer")
}
return b
}

View File

@@ -7,6 +7,7 @@ package field_test
import (
"database/sql"
"database/sql/driver"
"errors"
"net"
"net/http"
"net/url"
@@ -470,6 +471,45 @@ func (Role) Values() []string {
return []string{"admin", "owner"}
}
type RoleInt int32
func (RoleInt) Values() []string {
return []string{"unknown", "admin", "owner"}
}
func (i RoleInt) String() string {
switch i {
case 1:
return "admin"
case 2:
return "owner"
default:
return "unknown"
}
}
func (i RoleInt) Value() (driver.Value, error) {
return i.String(), nil
}
func (i *RoleInt) Scan(val interface{}) error {
switch v := val.(type) {
case string:
switch v {
case "admin":
*i = 1
case "owner":
*i = 2
default:
*i = 0
}
default:
return errors.New("bad enum value")
}
return nil
}
func TestField_Enums(t *testing.T) {
fd := field.Enum("role").
Values(
@@ -505,6 +545,18 @@ func TestField_Enums(t *testing.T) {
assert.False(t, fd.Info.ValueScanner())
assert.Equal(t, "admin", fd.Enums[0].V)
assert.Equal(t, "owner", fd.Enums[1].V)
assert.False(t, fd.Info.Stringer())
fd = field.Enum("role").GoType(RoleInt(0)).Descriptor()
assert.Equal(t, "field_test.RoleInt", fd.Info.Ident)
assert.Equal(t, "entgo.io/ent/schema/field_test", fd.Info.PkgPath)
assert.Equal(t, "field_test.RoleInt", fd.Info.String())
assert.False(t, fd.Info.Nillable)
assert.True(t, fd.Info.ValueScanner())
assert.Equal(t, "unknown", fd.Enums[0].V)
assert.Equal(t, "admin", fd.Enums[1].V)
assert.Equal(t, "owner", fd.Enums[2].V)
assert.True(t, fd.Info.Stringer())
}
func TestField_UUID(t *testing.T) {