-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from lestrrat-go/genoptions
Add a genoption tool to automatically generate options
- Loading branch information
Showing
3 changed files
with
342 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
module github.com/lestrrat-go/option/cmd/genoptions | ||
|
||
go 1.17 | ||
|
||
require ( | ||
github.com/goccy/go-yaml v1.9.5 | ||
github.com/lestrrat-go/codegen v1.0.3 | ||
github.com/lestrrat-go/xstrings v0.0.0-20210804220435-4dd8b234342b | ||
) | ||
|
||
require ( | ||
github.com/fatih/color v1.10.0 // indirect | ||
github.com/lestrrat-go/option v1.0.0 // indirect | ||
github.com/mattn/go-colorable v0.1.8 // indirect | ||
github.com/mattn/go-isatty v0.0.12 // indirect | ||
github.com/pkg/errors v0.9.1 // indirect | ||
golang.org/x/mod v0.3.0 // indirect | ||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect | ||
golang.org/x/tools v0.0.0-20200918232735-d647fc253266 // indirect | ||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= | ||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= | ||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= | ||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= | ||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= | ||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= | ||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= | ||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= | ||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= | ||
github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0= | ||
github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= | ||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= | ||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= | ||
github.com/lestrrat-go/codegen v1.0.3 h1:HviEmojOS51tH5L762bRMAZPj8DTIkwk24Il8M+54sE= | ||
github.com/lestrrat-go/codegen v1.0.3/go.mod h1:q3mmYUQW1bg3Z74ap7RgaVv1LmrBtlAKQOElxZTKlRM= | ||
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4= | ||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= | ||
github.com/lestrrat-go/xstrings v0.0.0-20210804220435-4dd8b234342b h1:3laG8JWIeDGb7lf00nMRznLdCHy0aZPd/CGz7Okn1SY= | ||
github.com/lestrrat-go/xstrings v0.0.0-20210804220435-4dd8b234342b/go.mod h1:mPFmD3Wuy0ddyPFvllLq4sUpGfE40T3VE8kWWS8fxGA= | ||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= | ||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= | ||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= | ||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | ||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= | ||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= | ||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= | ||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
golang.org/x/tools v0.0.0-20200918232735-d647fc253266 h1:k7tVuG0g1JwmD3Jh8oAl1vQ1C3jb4Hi/dUl1wWDBJpQ= | ||
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= | ||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= | ||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"flag" | ||
"fmt" | ||
"os" | ||
"sort" | ||
"strings" | ||
|
||
"github.com/goccy/go-yaml" | ||
"github.com/lestrrat-go/codegen" | ||
"github.com/lestrrat-go/xstrings" | ||
) | ||
|
||
var objectsFile = flag.String(`objects`, `objects.yaml`, `specify file containing object definitions`) | ||
|
||
func main() { | ||
flag.Parse() | ||
|
||
if err := _main(); err != nil { | ||
fmt.Fprintf(os.Stderr, "%s\n", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func writeComment(o *codegen.Output, comment string) bool { | ||
comment = strings.TrimSpace(comment) | ||
if comment == "" { | ||
return false | ||
} | ||
for i, line := range strings.Split(comment, "\n") { | ||
if i == 0 { | ||
o.LL(`// %s`, line) | ||
} else { | ||
o.L(`// %s`, line) | ||
} | ||
} | ||
return true | ||
} | ||
|
||
type Objects struct { | ||
Output string | ||
PackageName string `yaml:"package_name"` | ||
Imports []string `yaml:"imports"` | ||
Interfaces []*struct { | ||
Name string | ||
Comment string | ||
ConcreteType string `yaml:"concrete_type"` | ||
Methods []string | ||
Embeds []string | ||
} `yaml:"interfaces"` | ||
Options []*struct { | ||
Ident string | ||
OptionName string `yaml:"option_name"` // usually "With" + $Ident | ||
SkipOption bool `yaml:"skip_option"` | ||
Interface string | ||
ConcreteType string | ||
Comment string | ||
ArgumentType string `yaml:"argument_type"` | ||
ConstantValue string `yaml:"constant_value"` | ||
} `yaml:"options"` | ||
} | ||
|
||
func _main() error { | ||
var objects Objects | ||
|
||
{ | ||
buf, err := os.ReadFile(*objectsFile) | ||
if err != nil { | ||
return err | ||
} | ||
if err := yaml.Unmarshal(buf, &objects); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
for _, iface := range objects.Interfaces { | ||
if iface.ConcreteType == "" { | ||
iface.ConcreteType = xstrings.LcFirst(iface.Name) | ||
} | ||
if len(iface.Methods) == 0 { | ||
iface.Methods = append(iface.Methods, iface.ConcreteType) | ||
} | ||
} | ||
|
||
for _, option := range objects.Options { | ||
if option.OptionName == "" { | ||
option.OptionName = `With` + option.Ident | ||
} | ||
if option.ConcreteType == "" { | ||
option.ConcreteType = xstrings.LcFirst(option.Interface) | ||
} | ||
} | ||
|
||
sort.Slice(objects.Interfaces, func(i, j int) bool { | ||
return objects.Interfaces[i].Name < objects.Interfaces[j].Name | ||
}) | ||
sort.Slice(objects.Options, func(i, j int) bool { | ||
return objects.Options[i].Ident < objects.Options[j].Ident | ||
}) | ||
|
||
if err := genOptions(&objects); err != nil { | ||
return fmt.Errorf(`failed to generate %q`, objects.Output) | ||
} | ||
|
||
if err := genOptionTests(&objects); err != nil { | ||
return fmt.Errorf(`failed to generate tests for %q`, objects.Output) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func genOptions(objects *Objects) error { | ||
var buf bytes.Buffer | ||
|
||
o := codegen.NewOutput(&buf) | ||
|
||
o.L("// This file is auto-generated by github.com/lestrrat-go/option/cmd/genoptions. DO NOT EDIT") | ||
|
||
o.LL(`package %s`, objects.PackageName) | ||
|
||
imports := append(objects.Imports, []string{ | ||
`github.com/lestrrat-go/jwx/v2/jwa`, | ||
`github.com/lestrrat-go/jwx/v2/jwe`, | ||
`github.com/lestrrat-go/jwx/v2/jwk`, | ||
`github.com/lestrrat-go/jwx/v2/jws`, | ||
`github.com/lestrrat-go/jwx/v2/jwt`, | ||
}...) | ||
// Write all imports -- they will be pruned by golang.org/x/tools/imports eventually, | ||
// so it's okay to be redundant | ||
o.WriteImports(imports...) | ||
|
||
o.LL(`type Option = option.Interface`) | ||
|
||
for _, iface := range objects.Interfaces { | ||
if writeComment(o, iface.Comment) { | ||
o.L(`type %s interface {`, iface.Name) | ||
} else { | ||
o.LL(`type %s interface {`, iface.Name) | ||
} | ||
if len(iface.Embeds) < 1 { | ||
o.L(`Option`) | ||
} else { | ||
for _, embed := range iface.Embeds { | ||
o.L(embed) | ||
} | ||
} | ||
|
||
for _, method := range iface.Methods { | ||
o.L(`%s()`, method) | ||
} | ||
o.L(`}`) | ||
|
||
o.LL(`type %s struct {`, iface.ConcreteType) | ||
o.L(`Option`) | ||
o.L(`}`) | ||
|
||
for _, method := range iface.Methods { | ||
o.LL(`func (*%s) %s() {}`, iface.ConcreteType, method) | ||
} | ||
} | ||
|
||
o.L(``) | ||
|
||
{ | ||
seen := make(map[string]struct{}) | ||
for _, option := range objects.Options { | ||
_, ok := seen[option.Ident] | ||
if !ok { | ||
o.L(`type ident%s struct{}`, option.Ident) | ||
seen[option.Ident] = struct{}{} | ||
} | ||
} | ||
} | ||
|
||
{ | ||
seen := make(map[string]struct{}) | ||
for _, option := range objects.Options { | ||
_, ok := seen[option.Ident] | ||
if !ok { | ||
o.LL(`func (ident%s) String() string {`, option.Ident) | ||
o.L(`return %q`, option.OptionName) | ||
o.L(`}`) | ||
seen[option.Ident] = struct{}{} | ||
} | ||
} | ||
} | ||
|
||
for _, option := range objects.Options { | ||
if option.SkipOption { | ||
continue | ||
} | ||
|
||
if writeComment(o, option.Comment) { | ||
o.L(`func %s(`, option.OptionName) | ||
} else { | ||
o.LL(`func %s(`, option.OptionName) | ||
} | ||
if argType := option.ArgumentType; argType != "" { | ||
o.R(`v %s`, argType) | ||
} | ||
o.R(`) %s {`, option.Interface) | ||
|
||
value := `v` | ||
if cv := option.ConstantValue; cv != "" { | ||
value = cv | ||
} | ||
|
||
o.L(`return &%s{option.New(ident%s{}, %s)}`, option.ConcreteType, option.Ident, value) | ||
o.L(`}`) | ||
} | ||
|
||
if err := o.WriteFile(objects.Output, codegen.WithFormatCode(true)); err != nil { | ||
if cfe, ok := err.(codegen.CodeFormatError); ok { | ||
fmt.Fprint(os.Stderr, cfe.Source()) | ||
} | ||
return fmt.Errorf(`failed to write to headers_gen.go: %w`, err) | ||
} | ||
return nil | ||
} | ||
|
||
func genOptionTests(objects *Objects) error { | ||
var buf bytes.Buffer | ||
|
||
o := codegen.NewOutput(&buf) | ||
|
||
o.L("// This file is auto-generated by internal/cmd/genoptions/main.go. DO NOT EDIT") | ||
|
||
o.LL(`package %s`, objects.PackageName) | ||
|
||
o.LL(`func TestOptionIdent(t *testing.T) {`) | ||
seen := make(map[string]struct{}) | ||
for _, option := range objects.Options { | ||
_, ok := seen[option.Ident] | ||
if ok { | ||
continue | ||
} | ||
|
||
o.L(`require.Equal(t, %q, ident%s{}.String())`, option.OptionName, option.Ident) | ||
seen[option.Ident] = struct{}{} | ||
} | ||
|
||
o.L(`}`) | ||
|
||
filename := strings.Replace(objects.Output, `.go`, `_test.go`, -1) | ||
if err := o.WriteFile(filename, codegen.WithFormatCode(true)); err != nil { | ||
if cfe, ok := err.(codegen.CodeFormatError); ok { | ||
fmt.Fprint(os.Stderr, cfe.Source()) | ||
} | ||
return fmt.Errorf(`failed to write to headers_gen.go: %w`, err) | ||
} | ||
|
||
return nil | ||
} |