Skip to content

Commit

Permalink
strict hseq selector assuming T and *T as different types
Browse files Browse the repository at this point in the history
  • Loading branch information
fogfish committed Nov 11, 2023
1 parent 8605b71 commit 4a85608
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 21 deletions.
31 changes: 10 additions & 21 deletions hseq/hseq.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@ import (
)

// Type element of product type, a type safe wrapper of reflect.StructField
// Type safe wrapper prevents reflect.StructField to be used outside of original type T context.
type Type[T any] struct {
reflect.StructField
PureType reflect.Type

ID int // TODO: rename to Index
PureType reflect.Type
ID int
}

// Heterogenous projection of product type
type Seq[T any] []Type[T]

// Unfold type T to heterogenous sequence
// Unfold type T to heterogenous sequence using field names
func New[T any](names ...string) Seq[T] {
cat := typeOf(*new(T))
cat := reflect.TypeOf(new(T)).Elem()
seq := make(Seq[T], 0)
seq = unfold(cat, seq)

Expand Down Expand Up @@ -64,15 +65,6 @@ func unfold[T any](cat reflect.Type, seq Seq[T]) Seq[T] {
return seq
}

func typeOf[T any](t T) reflect.Type {
typeof := reflect.TypeOf(t)
if /*typeof != nil &&*/ typeof.Kind() == reflect.Ptr {
typeof = typeof.Elem()
}

return typeof
}

// Unfold type T to heterogenous sequence
func New1[T, A any]() Seq[T] {
seq := New[T]()
Expand Down Expand Up @@ -183,20 +175,17 @@ func New9[T, A, B, C, D, E, F, G, H, I any]() Seq[T] {

// Lookup type heterogenous sequence by "witness" type
func ForType[A, T any](seq Seq[T]) Type[T] {
val := typeOf(new(A))
// Note: new(A) always create pointer to A (*A)
val := reflect.TypeOf(new(A)).Elem()

for _, f := range seq {
ft := f.Type
if ft.Kind() == reflect.Pointer {
ft = ft.Elem()
}

if ft.String() == val.String() && ft.AssignableTo(val) {
return f
}
}

cat := typeOf(*new(T))
cat := reflect.TypeOf(new(T)).Elem()
panic(fmt.Errorf("%s is not member of %s type", val.Name(), cat.Name()))
}

Expand All @@ -208,7 +197,7 @@ func ForName[T any](seq Seq[T], field string) Type[T] {
}
}

cat := typeOf(*new(T))
cat := reflect.TypeOf(new(T)).Elem()
panic(fmt.Errorf("%s is not member of %s type", field, cat.Name()))
}

Expand Down Expand Up @@ -243,7 +232,7 @@ func assertType[T, A any](t Type[T], strict bool) (string, reflect.Kind) {
}

if k.Kind() != a.Kind() {
s := typeOf(*new(T))
s := reflect.TypeOf(new(T)).Elem()
panic(
fmt.Errorf("type %s is not equal %s at %s.%s",
t.Type.Kind(), a.Kind(), s.Name(), t.StructField.Name,
Expand Down
164 changes: 164 additions & 0 deletions hseq/hseq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,170 @@ import (
"github.com/fogfish/it/v2"
)

//
// ForType[A, T] selector
//

func forType[A any]() func(t *testing.T) {
type T struct {
F1 A
F2 *A
F3 []A
F4 *[]A
}
seq := hseq.New[T]()

return func(t *testing.T) {
for expect, tt := range map[string]hseq.Type[T]{
"F1": hseq.ForType[A, T](seq),
"F2": hseq.ForType[*A, T](seq),
"F3": hseq.ForType[[]A, T](seq),
"F4": hseq.ForType[*[]A, T](seq),
} {
it.Then(t).Should(
it.Equal(tt.Name, expect),
)
}
}
}

func TestForType(t *testing.T) {
type S string
t.Run("String", forType[string]())
t.Run("StringAlias", forType[S]())
t.Run("Bool", forType[bool]())
t.Run("Int8", forType[int8]())
t.Run("UInt8", forType[uint8]())
t.Run("Byte", forType[byte]())
t.Run("Int16", forType[int16]())
t.Run("UInt16", forType[uint16]())
t.Run("Int32", forType[int32]())
t.Run("Rune", forType[rune]())
t.Run("UInt32", forType[uint32]())
t.Run("Int64", forType[int64]())
t.Run("UInt64", forType[uint64]())
t.Run("Int", forType[int]())
t.Run("UInt", forType[uint]())
t.Run("UIntPtr", forType[uintptr]())
t.Run("Float32", forType[float32]())
t.Run("Float64", forType[float64]())
t.Run("Complex64", forType[complex64]())
t.Run("Complex128", forType[complex128]())

type Struct struct{ A string }
t.Run("StructNoName", forType[struct{ A string }]())
t.Run("Struct", forType[Struct]())

type Interface interface{ A() string }
t.Run("InterfaceNoName", forType[interface{ A() string }]())
t.Run("Interface", forType[Interface]())

t.Run("Embedded", func(t *testing.T) {
type S string
type I int
type A struct{ S }
type B struct{ I }
type T struct {
A
*B
}

seq := hseq.New[T]()

for expect, tt := range map[string]hseq.Type[T]{
"S": hseq.ForType[S, T](seq),
"I": hseq.ForType[I, T](seq),
} {
it.Then(t).Should(
it.Equal(tt.Name, expect),
)
}
})
}

//
// ForName[T] selector
//

func forName[A any]() func(t *testing.T) {
type T struct {
F1 A
F2 *A
F3 []A
F4 *[]A
}
seq := hseq.New[T]()

return func(t *testing.T) {
for field, kind := range map[string]reflect.Type{
"F1": reflect.TypeOf(new(A)).Elem(),
"F2": reflect.TypeOf(new(*A)).Elem(),
"F3": reflect.TypeOf(new([]A)).Elem(),
"F4": reflect.TypeOf(new(*[]A)).Elem(),
} {
tt := hseq.ForName[T](seq, field)

it.Then(t).Should(
it.Equal(tt.Name, field),
it.Equal(tt.Type, kind),
)
}
}
}

func TestForName(t *testing.T) {
type S string
t.Run("String", forName[string]())
t.Run("StringAlias", forName[S]())
t.Run("Bool", forName[bool]())
t.Run("Int8", forName[int8]())
t.Run("UInt8", forName[uint8]())
t.Run("Byte", forName[byte]())
t.Run("Int16", forName[int16]())
t.Run("UInt16", forName[uint16]())
t.Run("Int32", forName[int32]())
t.Run("Rune", forName[rune]())
t.Run("UInt32", forName[uint32]())
t.Run("Int64", forName[int64]())
t.Run("UInt64", forName[uint64]())
t.Run("Int", forName[int]())
t.Run("UInt", forName[uint]())
t.Run("UIntPtr", forName[uintptr]())
t.Run("Float32", forName[float32]())
t.Run("Float64", forName[float64]())
t.Run("Complex64", forName[complex64]())
t.Run("Complex128", forName[complex128]())

type Struct struct{ A string }
t.Run("StructNoName", forName[struct{ A string }]())
t.Run("Struct", forName[Struct]())

type Interface interface{ A() string }
t.Run("InterfaceNoName", forName[interface{ A() string }]())
t.Run("Interface", forName[Interface]())

t.Run("Embedded", func(t *testing.T) {
type S string
type I int
type A struct{ S }
type B struct{ I }
type T struct {
A
*B
}

seq := hseq.New[T]()
it.Then(t).Should(
it.Equal(hseq.ForName[T](seq, "S").Name, "S"),
it.Equal(hseq.ForName[T](seq, "I").Name, "I"),
)
})
}

//
// New
//

type T1 string
type T2 string
type Foo struct{ T1 }
Expand Down

0 comments on commit 4a85608

Please sign in to comment.