├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── type_registry.go └── type_registry_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - "1.13" 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Jinxin Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # a library to create type dynamically 2 | 3 | [![Report](https://goreportcard.com/badge/github.com/xiaoxin01/typeregistry)](https://goreportcard.com/badge/github.com/xiaoxin01/typeregistry) 4 | [![Build Status](https://travis-ci.org/xiaoxin01/typeregistry.svg?branch=master)](https://travis-ci.org/xiaoxin01/typeregistry) 5 | 6 | ## how to use 7 | 8 | ```go 9 | // 1. define struct 10 | type Student struct { 11 | Age int 12 | Name string 13 | } 14 | 15 | // 2. add reflect type 16 | key := AddType(new(Student)) 17 | 18 | // 3. create struct from registed key 19 | student := Make(key) 20 | ``` 21 | 22 | ## custom registed key 23 | 24 | ```go 25 | var i interface{} = new(Student) 26 | 27 | // use lowercase struct name as key 28 | name := AddTypeWithKey(i, func(i interface{}) string { 29 | tpe := reflect.TypeOf(i).Elem() 30 | return strings.ToLower(tpe.Name()) 31 | }) 32 | 33 | student, ok := Create("student").(*Student) 34 | ``` 35 | 36 | ## benchmark 37 | 38 | ```bash 39 | go.exe test -benchmem -run=^$ supperxin/typeregistry -bench ^BenchmarkMake$ 40 | 41 | goos: windows 42 | goarch: amd64 43 | pkg: supperxin/typeregistry 44 | BenchmarkMake-8 8823256 138 ns/op 64 B/op 2 allocs/op 45 | PASS 46 | ok supperxin/typeregistry 1.551s 47 | ``` 48 | 49 | ```bash 50 | go.exe test -benchmem -run=^$ supperxin/typeregistry -bench ^BenchmarkCreateDirectly$ 51 | 52 | goos: windows 53 | goarch: amd64 54 | pkg: supperxin/typeregistry 55 | BenchmarkCreateDirectly-8 1000000000 0.593 ns/op 0 B/op 0 allocs/op 56 | PASS 57 | ok supperxin/typeregistry 0.857s 58 | ``` 59 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xiaoxin01/typeregistry 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/google/go-cmp v0.4.0 // indirect 7 | github.com/pkg/errors v0.9.0 // indirect 8 | gotest.tools v2.2.0+incompatible 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 2 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 3 | github.com/pkg/errors v0.9.0 h1:J8lpUdobwIeCI7OiSxHqEwJUKvJwicL5+3v1oe2Yb4k= 4 | github.com/pkg/errors v0.9.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 5 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 6 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 7 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 8 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 9 | -------------------------------------------------------------------------------- /type_registry.go: -------------------------------------------------------------------------------- 1 | package typeregistry 2 | 3 | import "reflect" 4 | 5 | var typeRegistry = make(map[string]reflect.Type) 6 | 7 | func init() { 8 | 9 | } 10 | 11 | // AddTypes add multiple types to registry center 12 | func AddTypes(interfaces []interface{}) { 13 | for _, i := range interfaces { 14 | AddType(i) 15 | } 16 | } 17 | 18 | // AddTypesWithKey add type to registry center with key generator func 19 | func AddTypesWithKey(interfaces []interface{}, keyFunc func(i interface{}) string) { 20 | for _, i := range interfaces { 21 | AddTypeWithKey(i, keyFunc) 22 | } 23 | } 24 | 25 | // AddType add type to registry center 26 | func AddType(i interface{}) string { 27 | return AddTypeWithKey(i, nil) 28 | } 29 | 30 | // AddTypeWithKey add type to registry center with key generator func 31 | func AddTypeWithKey(i interface{}, keyFunc func(i interface{}) string) string { 32 | var key string 33 | tpe := reflect.TypeOf(i) 34 | if keyFunc == nil { 35 | switch tpe.Kind() { 36 | // case reflect.Ptr: 37 | // key = reflect.ValueOf(i).Type().Name() 38 | default: 39 | key = tpe.String() 40 | } 41 | } else { 42 | key = keyFunc(i) 43 | } 44 | 45 | typeRegistry[key] = tpe 46 | 47 | return key 48 | } 49 | 50 | // CleanRegistry clean registered types 51 | func CleanRegistry() { 52 | if len(typeRegistry) > 0 { 53 | typeRegistry = make(map[string]reflect.Type) 54 | } 55 | } 56 | 57 | // RegistryLen return count of registied type 58 | func RegistryLen() int { 59 | return len(typeRegistry) 60 | } 61 | 62 | // Create create type by key 63 | // If type is pointer, Make will create an object, point to it and return none null pointer 64 | func Create(key string) interface{} { 65 | var value reflect.Value 66 | if tpe, ok := typeRegistry[key]; ok { 67 | value = reflect.New(tpe).Elem() 68 | switch tpe.Kind() { 69 | case reflect.Ptr: 70 | tValue := reflect.New(tpe.Elem()).Elem().Addr() 71 | value.Set(tValue) 72 | default: 73 | } 74 | return value.Interface() 75 | } 76 | 77 | return nil 78 | } 79 | 80 | // CreateSlice create slice type by key 81 | func CreateSlice(key string) interface{} { 82 | var value reflect.Value 83 | if tpe, ok := typeRegistry[key]; ok { 84 | value = reflect.MakeSlice(reflect.SliceOf(tpe), 0, 0) 85 | // switch tpe.Kind() { 86 | // case reflect.Ptr: 87 | // tValue := reflect.New(tpe.Elem()).Elem().Addr() 88 | // value.Set(tValue) 89 | // default: 90 | // } 91 | return value.Interface() 92 | } 93 | 94 | return nil 95 | } 96 | 97 | // GetLen return lengh of slice stored in interface 98 | func GetLen(i interface{}) int { 99 | value := reflect.ValueOf(i) 100 | 101 | if value.Kind() == reflect.Invalid { 102 | return -1 103 | } 104 | 105 | switch reflect.TypeOf(i).Kind() { 106 | case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: 107 | return value.Len() 108 | default: 109 | return -1 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /type_registry_test.go: -------------------------------------------------------------------------------- 1 | package typeregistry 2 | 3 | import ( 4 | "reflect" 5 | "strings" 6 | "testing" 7 | 8 | "gotest.tools/assert" 9 | ) 10 | 11 | type Student struct { 12 | Age int 13 | Name string 14 | } 15 | 16 | func TestAddTypes(t *testing.T) { 17 | CleanRegistry() 18 | t.Run("test add multiple basic types", func(t *testing.T) { 19 | types := []interface{}{} 20 | types = append(types, 21 | 1, 22 | 3.14, 23 | new(int), 24 | ) 25 | assert.Equal(t, 0, RegistryLen()) 26 | 27 | AddTypes(types) 28 | 29 | assert.Equal(t, 3, len(types)) 30 | assert.Equal(t, 3, RegistryLen()) 31 | }) 32 | } 33 | 34 | func TestAddType(t *testing.T) { 35 | CleanRegistry() 36 | t.Run("test add basic types", func(t *testing.T) { 37 | intKey := AddType(1) 38 | floatKey := AddType(3.14) 39 | intPtrKey := AddType(new(int)) 40 | 41 | assert.Equal(t, "int", intKey) 42 | assert.Equal(t, "float64", floatKey) 43 | assert.Equal(t, "*int", intPtrKey) 44 | }) 45 | 46 | t.Run("test add struct types", func(t *testing.T) { 47 | structKey := AddType(Student{}) 48 | structPtrKey := AddType(new(Student)) 49 | 50 | assert.Equal(t, "typeregistry.Student", structKey) 51 | assert.Equal(t, "*typeregistry.Student", structPtrKey) 52 | }) 53 | 54 | t.Run("test add basic slice types", func(t *testing.T) { 55 | sliceKey := AddType([]int{}) 56 | slicePtrKey := AddType([]*int{}) 57 | 58 | assert.Equal(t, "[]int", sliceKey) 59 | assert.Equal(t, "[]*int", slicePtrKey) 60 | }) 61 | 62 | t.Run("test add struct slice types", func(t *testing.T) { 63 | sliceKey := AddType([]Student{}) 64 | slicePtrKey := AddType([]*Student{}) 65 | 66 | assert.Equal(t, "[]typeregistry.Student", sliceKey) 67 | assert.Equal(t, "[]*typeregistry.Student", slicePtrKey) 68 | }) 69 | } 70 | 71 | func TestAddTypeWithKey(t *testing.T) { 72 | CleanRegistry() 73 | t.Run("test use struct lowercase name for key", func(t *testing.T) { 74 | var i interface{} = new(Student) 75 | name := AddTypeWithKey(i, func(i interface{}) string { 76 | tpe := reflect.TypeOf(i).Elem() 77 | return strings.ToLower(tpe.Name()) 78 | }) 79 | 80 | student, ok := Create("student").(*Student) 81 | 82 | assert.Equal(t, "student", name) 83 | assert.Equal(t, true, ok) 84 | assert.Equal(t, 0, student.Age) 85 | }) 86 | } 87 | 88 | func TestAddTypesWithKey(t *testing.T) { 89 | CleanRegistry() 90 | t.Run("test use struct lowercase name for key", func(t *testing.T) { 91 | var is []interface{} = []interface{}{new(Student)} 92 | AddTypesWithKey(is, func(i interface{}) string { 93 | tpe := reflect.TypeOf(i).Elem() 94 | return strings.ToLower(tpe.Name()) 95 | }) 96 | 97 | student, ok := Create("student").(*Student) 98 | 99 | assert.Equal(t, true, ok) 100 | assert.Equal(t, 0, student.Age) 101 | }) 102 | } 103 | 104 | func TestCleanRegistry(t *testing.T) { 105 | CleanRegistry() 106 | AddType(1) 107 | 108 | assert.Equal(t, 1, RegistryLen()) 109 | 110 | CleanRegistry() 111 | 112 | assert.Equal(t, 0, RegistryLen()) 113 | } 114 | 115 | func TestCreate(t *testing.T) { 116 | CleanRegistry() 117 | t.Run("test make basic type", func(t *testing.T) { 118 | AddType(1) 119 | AddType(3.14) 120 | 121 | in := Create("int") 122 | fl := Create("float64") 123 | 124 | assert.Equal(t, reflect.Int, reflect.TypeOf(in).Kind()) 125 | assert.Equal(t, 0, in.(int)) 126 | assert.Equal(t, reflect.Float64, reflect.TypeOf(fl).Kind()) 127 | assert.Equal(t, 0.0, fl.(float64)) 128 | }) 129 | 130 | t.Run("test make struct type", func(t *testing.T) { 131 | AddType(Student{}) 132 | AddType(new(Student)) 133 | 134 | student := Create("typeregistry.Student") 135 | studentPtr := Create("*typeregistry.Student") 136 | 137 | assert.Equal(t, reflect.Struct, reflect.TypeOf(student).Kind()) 138 | assert.Equal(t, 0, student.(Student).Age) 139 | assert.Equal(t, reflect.Ptr, reflect.TypeOf(studentPtr).Kind()) 140 | assert.Equal(t, 0, studentPtr.(*Student).Age) 141 | }) 142 | 143 | t.Run("test make slice", func(t *testing.T) { 144 | AddType([]int{}) 145 | AddType([]Student{}) 146 | AddType([]*Student{}) 147 | 148 | intSlice := Create("[]int") 149 | structSlice := Create("[]typeregistry.Student") 150 | structPtrSlice := Create("[]*typeregistry.Student") 151 | 152 | assert.Equal(t, reflect.Slice, reflect.TypeOf(intSlice).Kind()) 153 | assert.Equal(t, reflect.Slice, reflect.TypeOf(structSlice).Kind()) 154 | assert.Equal(t, reflect.Slice, reflect.TypeOf(structPtrSlice).Kind()) 155 | 156 | assert.Equal(t, 0, len(intSlice.([]int))) 157 | assert.Equal(t, 0, len(structSlice.([]Student))) 158 | assert.Equal(t, 0, len(structPtrSlice.([]*Student))) 159 | 160 | }) 161 | } 162 | 163 | func TestCreateSlice(t *testing.T) { 164 | CleanRegistry() 165 | t.Run("test make basic slice type", func(t *testing.T) { 166 | AddType(1) 167 | 168 | in := CreateSlice("int") 169 | 170 | assert.Equal(t, reflect.Slice, reflect.TypeOf(in).Kind()) 171 | assert.Equal(t, 0, len(in.([]int))) 172 | }) 173 | 174 | t.Run("test make struct slice type", func(t *testing.T) { 175 | AddType(Student{}) 176 | AddType(new(Student)) 177 | 178 | students := CreateSlice("typeregistry.Student") 179 | studentsPtr := CreateSlice("*typeregistry.Student") 180 | 181 | assert.Equal(t, reflect.Slice, reflect.TypeOf(students).Kind()) 182 | assert.Equal(t, reflect.Slice, reflect.TypeOf(studentsPtr).Kind()) 183 | 184 | _, studentsOk := students.([]Student) 185 | _, studentsPtrOk := studentsPtr.([]*Student) 186 | assert.Equal(t, true, studentsOk) 187 | assert.Equal(t, true, studentsPtrOk) 188 | }) 189 | } 190 | 191 | func TestGetLen(t *testing.T) { 192 | t.Run("get len of slice", func(t *testing.T) { 193 | intSlice := []int{1, 2, 3} 194 | len := GetLen(intSlice) 195 | 196 | assert.Equal(t, 3, len) 197 | }) 198 | 199 | t.Run("get len of invalid", func(t *testing.T) { 200 | nilLen := GetLen(nil) 201 | intLen := GetLen(1) 202 | 203 | assert.Equal(t, -1, nilLen) 204 | assert.Equal(t, -1, intLen) 205 | }) 206 | } 207 | 208 | func BenchmarkMake(b *testing.B) { 209 | CleanRegistry() 210 | key := AddType([]*Student{}) 211 | for n := 0; n < b.N; n++ { 212 | if Create(key) != nil { 213 | 214 | } 215 | } 216 | } 217 | 218 | func BenchmarkCreateDirectly(b *testing.B) { 219 | for n := 0; n < b.N; n++ { 220 | s := new([]*Student) 221 | if s != nil { 222 | 223 | } 224 | } 225 | } 226 | --------------------------------------------------------------------------------