-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathroute.go
130 lines (120 loc) · 3.79 KB
/
route.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
package yagclif
import (
"fmt"
"reflect"
"github.com/potatomasterrace/catch"
)
// route is an implementation of a cli route.
type route struct {
description string
formatedCallback func(args []string) error
parameterType reflect.Type
}
// Return the type of the custom argument.
// returns nil,nil if type of callback is func(somestruct,[]string).
func getCustomCallBackType(callBack interface{}) (reflect.Type, error) {
if callBack == nil {
return nil, fmt.Errorf("callback value cannot be nil")
}
callBackTipe := reflect.TypeOf(callBack)
if callBackTipe.Kind().String() == "func" {
switch callBackTipe.NumIn() {
case 1:
// instance of the expected type []string
if callBackTipe.AssignableTo(reflect.TypeOf(func([]string) {})) {
return nil, nil
}
case 2:
if callBackTipe.In(1) == reflect.TypeOf([]string{}) {
return callBackTipe.In(0), nil
}
}
}
return nil, fmt.Errorf(
"expected type func([]string) or func(SomeStruct,[]string) but instead found %s",
callBackTipe,
)
}
// getSimpleCallBack returns a function that calls the callbackFunction with remaining arguments.
func getSimpleCallBack(callBackFunctionValue reflect.Value) func(args []string) error {
return func(args []string) error {
err := catch.Error(func() {
arguments := make([]reflect.Value, 1)
arguments[0] = reflect.ValueOf(args)
callBackFunctionValue.Call(arguments)
})
if err != nil {
return fmt.Errorf("%s", err)
}
return nil
}
}
// getSimpleCallBack returns a function that calls the callbackFunction with an instance
// of its custom parameter and remaining arguments.
func getCustomCallBack(callBackFunctionValue reflect.Value, callBackCustomType reflect.Type) (callback func(args []string) error, err error) {
params, err := newParameters(callBackCustomType)
if err != nil {
return nil, err
}
firstParamInstance := reflect.New(callBackCustomType)
return func(args []string) error {
remainingArgs, err := params.ParseArguments(firstParamInstance.Interface(), args)
if err != nil {
return err
}
arguments := make([]reflect.Value, 2)
arguments[0] = firstParamInstance.Elem()
arguments[1] = reflect.ValueOf(remainingArgs)
_, callError := catch.Panic(func() {
callBackFunctionValue.Call(arguments)
})
if callError != nil {
return fmt.Errorf("%s", callError)
}
return nil
}, nil
}
// formatCallBack formats the callback function into a func(args []string)error that executes the callback with arguments.
func formatCallBack(callBackFunctionValue reflect.Value, callBackArgType reflect.Type) (executeCallback func(args []string) error, err error) {
if callBackArgType == nil {
return getSimpleCallBack(callBackFunctionValue), nil
}
return getCustomCallBack(callBackFunctionValue, callBackArgType)
}
// newRoute creates a new route.
func newRoute(description string, callBack interface{}) (*route, error) {
callBackFunctionValue := reflect.ValueOf(callBack)
callBackArgType, err := getCustomCallBackType(callBack)
if err != nil {
return nil, err
}
formatedCallback, err := formatCallBack(callBackFunctionValue, callBackArgType)
if err != nil {
return nil, err
}
return &route{
description: description,
formatedCallback: formatedCallback,
parameterType: callBackArgType,
}, nil
}
// run executes the formated callback with the arguments.
func (r *route) run(args []string) error {
formatedCallback := r.formatedCallback
if formatedCallback != nil {
return formatedCallback(args)
}
return fmt.Errorf("callback not defined")
}
// getHelp returns an array string.
// Each element is a line of the help text.
func (r *route) getHelp() []string {
if r.parameterType == nil {
return []string{}
}
parameters, err := newParameters(r.parameterType)
if err != nil {
return []string{"Could not parse parameter type"}
}
return parameters.getHelp()
}