-
Notifications
You must be signed in to change notification settings - Fork 0
/
derive.go
240 lines (200 loc) · 6.23 KB
/
derive.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
package clap
import (
"errors"
"fmt"
"github.com/runaek/clap/internal/derive"
)
const (
ErrDeriverNotFound Error = "deriver not found"
)
func notFound(t Type, deriver string) error {
return fmt.Errorf("%w (%s): %s", ErrDeriverNotFound, t, deriver)
}
// A PositionalDeriver is responsible for constructing a PositionalArg dynamically.
type PositionalDeriver interface {
DerivePosition(v any, index int, opts ...Option) (IPositional, error)
}
// A KeyValueDeriver is responsible for constructing a KeyValueArg dynamically.
type KeyValueDeriver interface {
DeriveKeyValue(v any, name string, opts ...Option) (IKeyValue, error)
}
// A FlagDeriver is responsible for constructing a FlagArg dynamically.
type FlagDeriver interface {
DeriveFlag(v any, name string, opts ...Option) (IFlag, error)
}
// RegisterPositionalDeriver adds a PositionalDeriver to the program so that it can be detected
// and used within struct-tags.
func RegisterPositionalDeriver(name string, d PositionalDeriver) {
positionalDerivers[name] = d
}
// RegisterKeyValueDeriver adds a KeyValueDeriver to the program so that it can be detected
// and used within struct-tags.
func RegisterKeyValueDeriver(name string, d KeyValueDeriver) {
keyValueDerivers[name] = d
}
// RegisterFlagDeriver adds a FlagDeriver to the program so that it can be detected
// and used within struct-tags.
func RegisterFlagDeriver(name string, d FlagDeriver) {
flagDerivers[name] = d
}
// Derive attempts to construct a number of Arg dynamically from some struct-tags.
//
// The tags are applied on the fields to be bound to the Arg (i.e. at runtime, the Field will hold
// the value of the Arg). There are 4 formats for each of the types and all can be prefixed with a
// '!' which will mark the Arg as required:
//
// 1. KeyValueArg: `cli:"@<name>|<alias>:<deriver>"
//
// 2. FlagArg: `cli:"-<name>|<alias>:<deriver>"
//
// 3. PositionalArg: `cli:"#<index>:<deriver>"
//
// 4. PositionalArg(s): `cli:"#<index>...:<deriver>"
//
// Where the 'deriver' is the name assigned to the deriver when it was registered (using one of
// RegisterKeyValueDeriver, RegisterFlagDeriver or RegisterPositionalDeriver).
//
// Usage strings can be supplied by creating a string field in the struct called <Name>Usage, the
// same can be done for a default value (i.e. <Name>Default). At runtime, the values held by the
// variable will be used for the respective usage/default.
//
// Package `github.com/runaek/clap/pkg/derivers` provides support to a number of the
// built-in Go types. The program `github.com/runaek/clap/cmd/generate_derivers` is
// a codegen tool for helping generate FlagDeriver, KeyValueDeriver and PositionalDeriver
// implementations using a specified parse.Parse[T].
//
//
// type Args struct {
// // a "name" or "N" KeyValueArg
// Name string `cli:"!@name|N:string"`
//
// // usage/description for the KeyValueArg
// NameUsage string
//
// // default value for the KeyValueArg
// NameDefault string
//
// // a variable number of string args, starting from the first index
// Values []string `cli:"#1...:string"`
//
// // usage/description for the positional args
// ValuesUsage string
// }
//
// ...
//
// var (
// args = Args{
// NameUsage: "Supply your name.",
// NameDefault: "John",
// ValuesUsage: "A number of values to be supplied."
// }
//
// parser = clap.Must("demo", &args)
// )
//
// func main() {
// parser.Parse()
// parser.Ok()
//
// // use args
// }
// NOTE: Fields should be defined in the same order you would expect to add them to a Parser manually (i.e. Field
// referring to the 1st positional argument must come before the Field referring to the 2nd positional argument).
func Derive(source any) ([]Arg, error) {
return deriveArgs(source)
}
// DeriveAll is a convenient wrapper around Derive.
func DeriveAll(sources ...any) ([]Arg, error) {
out := make([]Arg, 0, len(sources))
for _, s := range sources {
if a, ok := s.(Arg); ok {
out = append(out, a)
continue
}
more, err := deriveArgs(s)
if err != nil {
return out, err
}
out = append(out, more...)
}
return out, nil
}
var (
positionalDerivers = map[string]PositionalDeriver{}
keyValueDerivers = map[string]KeyValueDeriver{}
flagDerivers = map[string]FlagDeriver{}
)
// deriveArgs is a helper function for reading and creating Arg implementations using struct-tags and
// FlagDeriver, KeyValueDeriver and PositionalDeriver implementations that have been registered.
func deriveArgs(src any) ([]Arg, error) {
program, err := derive.Parse(src)
if err != nil {
return nil, err
}
args := program.Args()
out := make([]Arg, len(args))
for i, v := range args {
opts := []Option{
WithUsage(v.Usage),
}
if v.Required {
opts = append(opts, AsRequired())
}
if v.Alias != "" {
opts = append(opts, WithAlias(v.Alias))
}
if v.Default != "" {
opts = append(opts, WithDefault(v.Default))
}
var arg Arg
switch v.Type {
case derive.KeyType:
deriver := getKVD(v.Deriver)
if deriver == nil {
return nil, notFound(KeyValueType, v.Deriver)
}
if a, err := deriver.DeriveKeyValue(v.Field(), v.Identifier, opts...); err != nil {
return nil, fmt.Errorf("unable to derive key-value argument: %w", err)
} else {
arg = a
}
case derive.PosType:
deriver := getPD(v.Deriver)
if deriver == nil {
return nil, notFound(PositionType, v.Deriver)
}
if a, err := deriver.DerivePosition(v.Field(), v.Pos(), opts...); err != nil {
return nil, fmt.Errorf("unable to derive positional argument: %w", err)
} else {
arg = a
}
case derive.FlagType:
deriver := getFD(v.Deriver)
if deriver == nil {
return nil, notFound(FlagType, v.Deriver)
}
if a, err := deriver.DeriveFlag(v.Field(), v.Identifier, opts...); err != nil {
return nil, fmt.Errorf("unable to derive flag argument: %w", err)
} else {
arg = a
}
default:
return nil, fmt.Errorf("invalid Type for derived Argument: %s", v.Type)
}
if arg == nil {
return nil, errors.New("an error occurred deriving an Argument")
}
out[i] = arg
}
return out, nil
}
func getKVD(name string) KeyValueDeriver {
return keyValueDerivers[name]
}
func getPD(name string) PositionalDeriver {
return positionalDerivers[name]
}
func getFD(name string) FlagDeriver {
return flagDerivers[name]
}