Skip to content

Commit 2f34771

Browse files
committed
feat(ps-gen): create param set generator (#6)
1 parent d27e4e9 commit 2f34771

File tree

5 files changed

+254
-18
lines changed

5 files changed

+254
-18
lines changed

.golangci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ linters:
6969
linters:
7070
- revive
7171
text: 'dot-imports'
72-
# errcheck: We need to temporarily disable checking of auto generated
73-
# code, until the new generator has been created
7472
- path: '-auto\.go'
7573
linters:
7674
- errcheck

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"stylecheck",
7272
"subosito",
7373
"thelper",
74+
"tmpl",
7475
"toplevel",
7576
"tparallel",
7677
"typecheck",

Taskfile.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ tasks:
146146
- go tool cover -html=./coverage.out -o {{.COVER_HTML_PATH}}
147147
- open {{.COVER_HTML_PATH}}
148148

149+
# === ps-gen (param set generator) =========================
150+
151+
ps-gen:
152+
cmds:
153+
- go run ./cmd/ps-gen/main.go
154+
149155
# === i18n =================================================
150156

151157
clear:

assist/param-set-auto.go

Lines changed: 2 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/ps-gen/main.go

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
// ps-gen generates the file param-set-auto.go which contains all the
2+
// Bind* methods on ParamSet[N]. To regenerate, run:
3+
//
4+
// go run ./cmd/ps-gen/main.go
5+
//
6+
// The output is written to: assist/param-set-auto.go
7+
package main
8+
9+
import (
10+
"bytes"
11+
"fmt"
12+
"go/format"
13+
"log"
14+
"os"
15+
"text/template"
16+
)
17+
18+
// outputPath is the destination file relative to the working directory from
19+
// which you invoke `go run`. Adjust this to match your repository layout.
20+
const outputPath = "assist/param-set-auto.go"
21+
22+
// typeSpec describes a single Bind* method (or Bind*Slice pair).
23+
type typeSpec struct {
24+
// GoType is the Go type name used in the method signature, e.g. "bool",
25+
// "int32", "net.IPMask".
26+
GoType string
27+
28+
// FlagType is the pflag method suffix, e.g. "Bool", "Int32", "IPMask".
29+
// For BindEnum this differs from the method name.
30+
FlagType string
31+
32+
// MethodName overrides the generated Bind<MethodName> name when it differs
33+
// from FlagType (only needed for BindEnum).
34+
MethodName string
35+
36+
// HasSlice indicates whether a Bind*Slice variant should also be generated.
37+
HasSlice bool
38+
39+
// SliceGoType overrides the slice element type when it differs from GoType
40+
// (not currently needed, but kept for extensibility).
41+
SliceGoType string
42+
43+
// Comment is the doc-comment body (the part after "binds ").
44+
Comment string
45+
46+
// SliceComment is the doc-comment body for the slice variant.
47+
SliceComment string
48+
}
49+
50+
func (s typeSpec) Resolve() typeSpec {
51+
if s.MethodName == "" {
52+
s.MethodName = s.FlagType
53+
}
54+
if s.SliceGoType == "" {
55+
s.SliceGoType = s.GoType
56+
}
57+
return s
58+
}
59+
60+
var specs = []typeSpec{
61+
{
62+
GoType: "bool",
63+
FlagType: "Bool",
64+
HasSlice: true,
65+
Comment: "bool slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
66+
SliceComment: "[]bool slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
67+
},
68+
{
69+
GoType: "time.Duration",
70+
FlagType: "Duration",
71+
HasSlice: true,
72+
Comment: "time.Duration slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
73+
SliceComment: "[]time.Duration slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
74+
},
75+
{
76+
GoType: "string",
77+
FlagType: "String",
78+
MethodName: "Enum",
79+
HasSlice: false,
80+
Comment: "enum slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
81+
},
82+
{
83+
GoType: "float32",
84+
FlagType: "Float32",
85+
HasSlice: true,
86+
Comment: "float32 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
87+
SliceComment: "[]float32 slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
88+
},
89+
{
90+
GoType: "float64",
91+
FlagType: "Float64",
92+
HasSlice: true,
93+
Comment: "float64 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
94+
SliceComment: "[]float64 slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
95+
},
96+
{
97+
GoType: "int",
98+
FlagType: "Int",
99+
HasSlice: true,
100+
Comment: "int slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
101+
SliceComment: "[]int slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
102+
},
103+
{
104+
GoType: "int16",
105+
FlagType: "Int16",
106+
HasSlice: false,
107+
Comment: "int16 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
108+
},
109+
{
110+
GoType: "int32",
111+
FlagType: "Int32",
112+
HasSlice: true,
113+
Comment: "int32 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
114+
SliceComment: "[]int32 slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
115+
},
116+
{
117+
GoType: "int64",
118+
FlagType: "Int64",
119+
HasSlice: true,
120+
Comment: "int64 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
121+
SliceComment: "[]int64 slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
122+
},
123+
{
124+
GoType: "int8",
125+
FlagType: "Int8",
126+
HasSlice: false,
127+
Comment: "int8 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
128+
},
129+
{
130+
GoType: "net.IPMask",
131+
FlagType: "IPMask",
132+
HasSlice: false,
133+
Comment: "net.IPMask slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
134+
},
135+
{
136+
GoType: "net.IPNet",
137+
FlagType: "IPNet",
138+
HasSlice: false,
139+
Comment: "net.IPNet slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
140+
},
141+
{
142+
GoType: "string",
143+
FlagType: "String",
144+
HasSlice: true,
145+
Comment: "string slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
146+
SliceComment: "[]string slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
147+
},
148+
{
149+
GoType: "uint16",
150+
FlagType: "Uint16",
151+
HasSlice: false,
152+
Comment: "uint16 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
153+
},
154+
{
155+
GoType: "uint32",
156+
FlagType: "Uint32",
157+
HasSlice: false,
158+
Comment: "uint32 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
159+
},
160+
{
161+
GoType: "uint64",
162+
FlagType: "Uint64",
163+
HasSlice: false,
164+
Comment: "uint64 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
165+
},
166+
{
167+
GoType: "uint8",
168+
FlagType: "Uint8",
169+
HasSlice: false,
170+
Comment: "uint8 slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
171+
},
172+
{
173+
GoType: "uint",
174+
FlagType: "Uint",
175+
HasSlice: true,
176+
Comment: "uint slice flag with a shorthand if\n// 'info.Short' has been set otherwise binds without a short name.",
177+
SliceComment: "[]uint slice flag with a shorthand if 'info.Short' has been set\n// otherwise binds without a short name.",
178+
},
179+
}
180+
181+
const tmplSrc = `// Code generated by gen-param-set; DO NOT EDIT.
182+
183+
package assist
184+
185+
import (
186+
"net"
187+
"time"
188+
)
189+
{{range .}}{{$s := .}}
190+
// Bind{{$s.MethodName}} binds {{$s.Comment}}
191+
func (params *ParamSet[N]) Bind{{$s.MethodName}}(info *FlagInfo, to *{{$s.GoType}}) *ParamSet[N] {
192+
flagSet := params.ResolveFlagSet(info)
193+
if info.Short == "" {
194+
flagSet.{{$s.FlagType}}Var(to, info.FlagName(), info.Default.({{$s.GoType}}), info.Usage)
195+
} else {
196+
flagSet.{{$s.FlagType}}VarP(to, info.FlagName(), info.Short, info.Default.({{$s.GoType}}), info.Usage)
197+
}
198+
199+
return params
200+
}
201+
{{if $s.HasSlice}}
202+
// Bind{{$s.MethodName}}Slice binds {{$s.SliceComment}}
203+
func (params *ParamSet[N]) Bind{{$s.MethodName}}Slice(info *FlagInfo, to *[]{{$s.SliceGoType}}) *ParamSet[N] {
204+
flagSet := params.ResolveFlagSet(info)
205+
if info.Short == "" {
206+
flagSet.{{$s.FlagType}}SliceVar(to, info.FlagName(), info.Default.([]{{$s.SliceGoType}}), info.Usage)
207+
} else {
208+
flagSet.{{$s.FlagType}}SliceVarP(to, info.FlagName(), info.Short, info.Default.([]{{$s.SliceGoType}}), info.Usage)
209+
}
210+
211+
return params
212+
}
213+
{{end}}{{end}}`
214+
215+
func main() {
216+
// Resolve all specs (fill in derived fields).
217+
resolved := make([]typeSpec, len(specs))
218+
for i, s := range specs {
219+
resolved[i] = s.Resolve()
220+
}
221+
222+
tmpl, err := template.New("param-set-auto").Parse(tmplSrc)
223+
if err != nil {
224+
log.Fatalf("parse template: %v", err)
225+
}
226+
227+
var buf bytes.Buffer
228+
if err = tmpl.Execute(&buf, resolved); err != nil {
229+
log.Fatalf("execute template: %v", err)
230+
}
231+
232+
// Run through gofmt so the output is canonical.
233+
formatted, err := format.Source(buf.Bytes())
234+
if err != nil {
235+
// Emit the raw output to make debugging easier.
236+
fmt.Fprintf(os.Stderr, "--- raw output ---\n%s\n--- end ---\n", buf.String())
237+
log.Fatalf("gofmt: %v", err)
238+
}
239+
240+
if err := os.WriteFile(outputPath, formatted, 0o644); err != nil { //nolint:gosec // ok
241+
log.Fatalf("write %s: %v", outputPath, err)
242+
}
243+
244+
fmt.Printf("wrote %s\n", outputPath)
245+
}

0 commit comments

Comments
 (0)