Skip to content

Commit a6d26bd

Browse files
committed
feat: add alias resolution function
Signed-off-by: Mark Sagi-Kazar <[email protected]>
1 parent 633e5d0 commit a6d26bd

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

alias.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright © 2014 Steve Francia <[email protected]>.
2+
//
3+
// Use of this source code is governed by an MIT-style
4+
// license that can be found in the LICENSE file.
5+
package cast
6+
7+
import (
8+
"reflect"
9+
"slices"
10+
)
11+
12+
var kindNames = []string{
13+
reflect.String: "string",
14+
reflect.Bool: "bool",
15+
reflect.Int: "int",
16+
reflect.Int8: "int8",
17+
reflect.Int16: "int16",
18+
reflect.Int32: "int32",
19+
reflect.Int64: "int64",
20+
reflect.Uint: "uint",
21+
reflect.Uint8: "uint8",
22+
reflect.Uint16: "uint16",
23+
reflect.Uint32: "uint32",
24+
reflect.Uint64: "uint64",
25+
reflect.Float32: "float32",
26+
reflect.Float64: "float64",
27+
}
28+
29+
var kinds = map[reflect.Kind]func(reflect.Value) any{
30+
reflect.String: func(v reflect.Value) any { return v.String() },
31+
reflect.Bool: func(v reflect.Value) any { return v.Bool() },
32+
reflect.Int: func(v reflect.Value) any { return int(v.Int()) },
33+
reflect.Int8: func(v reflect.Value) any { return int8(v.Int()) },
34+
reflect.Int16: func(v reflect.Value) any { return int16(v.Int()) },
35+
reflect.Int32: func(v reflect.Value) any { return int32(v.Int()) },
36+
reflect.Int64: func(v reflect.Value) any { return v.Int() },
37+
reflect.Uint: func(v reflect.Value) any { return uint(v.Uint()) },
38+
reflect.Uint8: func(v reflect.Value) any { return uint8(v.Uint()) },
39+
reflect.Uint16: func(v reflect.Value) any { return uint16(v.Uint()) },
40+
reflect.Uint32: func(v reflect.Value) any { return uint32(v.Uint()) },
41+
reflect.Uint64: func(v reflect.Value) any { return v.Uint() },
42+
reflect.Float32: func(v reflect.Value) any { return float32(v.Float()) },
43+
reflect.Float64: func(v reflect.Value) any { return v.Float() },
44+
}
45+
46+
// resolveAlias attempts to resolve a named type to its underlying basic type (if possible).
47+
//
48+
// Pointers are expected to be indirected by this point.
49+
func resolveAlias(i any) (any, bool) {
50+
if i == nil {
51+
return nil, false
52+
}
53+
54+
t := reflect.TypeOf(i)
55+
56+
// Not a named type
57+
if t.Name() == "" || slices.Contains(kindNames, t.Name()) {
58+
return i, false
59+
}
60+
61+
resolve, ok := kinds[t.Kind()]
62+
if !ok { // Not a supported kind
63+
return i, false
64+
}
65+
66+
v := reflect.ValueOf(i)
67+
68+
return resolve(v), true
69+
}

alias_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright © 2014 Steve Francia <[email protected]>.
2+
//
3+
// Use of this source code is governed by an MIT-style
4+
// license that can be found in the LICENSE file.
5+
package cast
6+
7+
import (
8+
"testing"
9+
10+
qt "github.com/frankban/quicktest"
11+
)
12+
13+
func TestAlias(t *testing.T) {
14+
type MyStruct struct{}
15+
16+
type MyString string
17+
type MyOtherString MyString
18+
type MyAliasString = MyOtherString
19+
20+
type MyBool bool
21+
type MyOtherBool MyBool
22+
type MyAliasBool = MyOtherBool
23+
24+
type MyInt int
25+
type MyInt8 int8
26+
type MyInt16 int16
27+
type MyInt32 int32
28+
type MyInt64 int64
29+
type MyUint uint
30+
type MyUint8 uint8
31+
type MyUint16 uint16
32+
type MyUint32 uint32
33+
type MyUint64 uint64
34+
type MyFloat32 float32
35+
type MyFloat64 float64
36+
37+
testCases := []struct {
38+
input any
39+
expectedValue any
40+
expectedOk bool
41+
}{
42+
{"string", "string", false}, // Already resolved
43+
{MyStruct{}, MyStruct{}, false}, // Non-resolvable
44+
45+
{MyString("string"), "string", true},
46+
{MyOtherString("string"), "string", true},
47+
{MyAliasString("string"), "string", true},
48+
49+
{MyBool(true), true, true},
50+
{MyOtherBool(true), true, true},
51+
{MyAliasBool(true), true, true},
52+
53+
{MyInt(1234), int(1234), true},
54+
{MyInt8(123), int8(123), true},
55+
{MyInt16(1234), int16(1234), true},
56+
{MyInt32(1234), int32(1234), true},
57+
{MyInt64(1234), int64(1234), true},
58+
59+
{MyUint(1234), uint(1234), true},
60+
{MyUint8(123), uint8(123), true},
61+
{MyUint16(1234), uint16(1234), true},
62+
{MyUint32(1234), uint32(1234), true},
63+
{MyUint64(1234), uint64(1234), true},
64+
65+
{MyFloat32(1.0), float32(1.0), true},
66+
{MyFloat64(1.0), float64(1.0), true},
67+
}
68+
69+
for _, testCase := range testCases {
70+
// TODO: remove after minimum Go version is >=1.22
71+
testCase := testCase
72+
73+
t.Run("", func(t *testing.T) {
74+
c := qt.New(t)
75+
76+
actualValue, ok := resolveAlias(testCase.input)
77+
78+
c.Assert(actualValue, qt.Equals, testCase.expectedValue)
79+
c.Assert(ok, qt.Equals, testCase.expectedOk)
80+
})
81+
}
82+
}

0 commit comments

Comments
 (0)