This repository was archived by the owner on Feb 9, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconstructor.go
149 lines (141 loc) · 4.02 KB
/
constructor.go
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
package kinitx
import (
"reflect"
"github.com/go-kata/kdone"
"github.com/go-kata/kerror"
)
// Constructor represents a constructor based on a function.
type Constructor struct {
// t specifies the type of an object that is created by this constructor.
t reflect.Type
// function specifies the reflection to a function value.
function reflect.Value
// inTypes specifies types of function input parameters.
inTypes []reflect.Type
// objectOutIndex specifies the index of a function output parameter that contains a created object.
objectOutIndex int
// destructorOutIndex specifies the index of a function output parameter that contains a destructor.
// The value -1 means that a function doesn't return a destructor.
destructorOutIndex int
// errorOutIndex specifies the index of a function output parameter that contains an error.
// The value -1 means that a function doesn't return an error.
errorOutIndex int
}
// NewConstructor returns a new constructor.
//
// The argument x must be a function that is compatible with one of following signatures
// (T is an arbitrary Go type):
//
// func(...) T;
//
// func(...) (T, error);
//
// func(...) (T, kdone.Destructor, error).
//
func NewConstructor(x interface{}) (*Constructor, error) {
if x == nil {
return nil, kerror.New(kerror.EViolation, "function expected, nil given")
}
ft := reflect.TypeOf(x)
fv := reflect.ValueOf(x)
if ft.Kind() != reflect.Func {
return nil, kerror.Newf(kerror.EViolation, "function expected, %s given", ft)
}
if fv.IsNil() {
return nil, kerror.New(kerror.EViolation, "function expected, nil given")
}
c := &Constructor{
function: fv,
}
numIn := ft.NumIn()
if ft.IsVariadic() {
numIn--
}
c.inTypes = make([]reflect.Type, numIn)
for i := 0; i < numIn; i++ {
c.inTypes[i] = ft.In(i)
}
switch ft.NumOut() {
default:
return nil, kerror.Newf(kerror.EViolation, "function %s is not a constructor", ft)
case 1:
c.t = ft.Out(0)
c.objectOutIndex = 0
c.destructorOutIndex = -1
c.errorOutIndex = -1
case 2:
if ft.Out(1) != errorType {
return nil, kerror.Newf(kerror.EViolation, "function %s is not a constructor", ft)
}
c.t = ft.Out(0)
c.objectOutIndex = 0
c.destructorOutIndex = -1
c.errorOutIndex = 1
case 3:
if ft.Out(1) != destructorType || ft.Out(2) != errorType {
return nil, kerror.Newf(kerror.EViolation, "function %s is not a constructor", ft)
}
c.t = ft.Out(0)
c.objectOutIndex = 0
c.destructorOutIndex = 1
c.errorOutIndex = 2
}
return c, nil
}
// MustNewConstructor is a variant of the NewConstructor that panics on error.
func MustNewConstructor(x interface{}) *Constructor {
c, err := NewConstructor(x)
if err != nil {
panic(err)
}
return c
}
// Type implements the kinit.Constructor interface.
func (c *Constructor) Type() reflect.Type {
if c == nil {
return nil
}
return c.t
}
// Parameters implements the kinit.Constructor interface.
func (c *Constructor) Parameters() []reflect.Type {
if c == nil {
return nil
}
types := make([]reflect.Type, len(c.inTypes))
copy(types, c.inTypes)
return types
}
// Create implements the kinit.Constructor interface.
func (c *Constructor) Create(a ...reflect.Value) (reflect.Value, kdone.Destructor, error) {
if c == nil {
return reflect.Value{}, kdone.Noop, nil
}
if len(a) != len(c.inTypes) {
return reflect.Value{}, kdone.Noop, kerror.Newf(kerror.EViolation,
"%s constructor expects %d argument(s), %d given",
c.t, len(c.inTypes), len(a))
}
for i, v := range a {
if v.Type() != c.inTypes[i] {
return reflect.Value{}, kdone.Noop, kerror.Newf(kerror.EViolation,
"%s constructor expects argument %d to be of %s type, %s given",
c.t, i+1, c.inTypes[i], v.Type())
}
}
out := c.function.Call(a)
obj := out[c.objectOutIndex]
var dtor kdone.Destructor = kdone.Noop
if c.destructorOutIndex >= 0 {
if v := out[c.destructorOutIndex].Interface(); v != nil {
dtor = v.(kdone.Destructor)
}
}
var err error
if c.errorOutIndex >= 0 {
if v := out[c.errorOutIndex].Interface(); v != nil {
err = v.(error)
}
}
return obj, dtor, err
}