pax_global_header00006660000000000000000000000064146425221330014514gustar00rootroot0000000000000052 comment=9e2df336677fea59b8510b2e46294de6c413d09f golang-github-lestrrat-go-option-1.0.1/000077500000000000000000000000001464252213300200115ustar00rootroot00000000000000golang-github-lestrrat-go-option-1.0.1/.github/000077500000000000000000000000001464252213300213515ustar00rootroot00000000000000golang-github-lestrrat-go-option-1.0.1/.github/workflows/000077500000000000000000000000001464252213300234065ustar00rootroot00000000000000golang-github-lestrrat-go-option-1.0.1/.github/workflows/ci.yml000066400000000000000000000016021464252213300245230ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: go: [ '1.19', '1.18' ] name: Go ${{ matrix.go }} test steps: - name: Checkout repository uses: actions/checkout@v3 - name: Cache Go modules uses: actions/cache@v3 with: path: | ~/go/pkg/mod ~/.cache/go-build key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Install Go stable version uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} check-latest: true - name: Test run: go test -v -race ./... - name: Upload code coverage to codecov if: matrix.go == '1.19' uses: codecov/codecov-action@v3 with: file: ./coverage.out golang-github-lestrrat-go-option-1.0.1/.github/workflows/lint.yml000066400000000000000000000005761464252213300251070ustar00rootroot00000000000000name: lint on: [push] jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version: 1.19 check-latest: true - uses: golangci/golangci-lint-action@v3 with: version: v1.49.0 - name: Run go vet run: | go vet ./... golang-github-lestrrat-go-option-1.0.1/.gitignore000066400000000000000000000004151464252213300220010ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ golang-github-lestrrat-go-option-1.0.1/LICENSE000066400000000000000000000020541464252213300210170ustar00rootroot00000000000000MIT License Copyright (c) 2021 lestrrat-go Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-lestrrat-go-option-1.0.1/README.md000066400000000000000000000170761464252213300213030ustar00rootroot00000000000000# option Base object for the "Optional Parameters Pattern". # DESCRIPTION The beauty of this pattern is that you can achieve a method that can take the following simple calling style ```go obj.Method(mandatory1, mandatory2) ``` or the following, if you want to modify its behavior with optional parameters ```go obj.Method(mandatory1, mandatory2, optional1, optional2, optional3) ``` Instead of the more clunky zero value for optionals style ```go obj.Method(mandatory1, mandatory2, nil, "", 0) ``` or the equally clunky config object style, which requires you to create a struct with `NamesThatLookReallyLongBecauseItNeedsToIncludeMethodNamesConfig ```go cfg := &ConfigForMethod{ Optional1: ..., Optional2: ..., Optional3: ..., } obj.Method(mandatory1, mandatory2, &cfg) ``` # SYNOPSIS Create an "identifier" for the option. We recommend using an unexported empty struct, because 1. It is uniquely identifiable globally 1. Takes minimal space 1. Since it's unexported, you do not have to worry about it leaking elsewhere or having it changed by consumers ```go // an unexported empty struct type identFeatureX struct{} ``` Then define a method to create an option using this identifier. Here we assume that the option will be a boolean option. ```go // this is optional, but for readability we usually use a wrapper // around option.Interface, or a type alias. type Option func WithFeatureX(v bool) Option { // use the constructor to create a new option return option.New(identFeatureX{}, v) } ``` Now you can create an option, which essentially a two element tuple consisting of an identifier and its associated value. To consume this, you will need to create a function with variadic parameters, and iterate over the list looking for a particular identifier: ```go func MyAwesomeFunc( /* mandatory parameters omitted */, options ...[]Option) { var enableFeatureX bool // The nolint directive is recommended if you are using linters such // as golangci-lint //nolint:forcetypeassert for _, option := range options { switch option.Ident() { case identFeatureX{}: enableFeatureX = option.Value().(bool) // other cases omitted } } if enableFeatureX { .... } } ``` # Option objects Option objects take two arguments, its identifier and the value it contains. The identifier can be anything, but it's usually better to use a an unexported empty struct so that only you have the ability to generate said option: ```go type identOptionalParamOne struct{} type identOptionalParamTwo struct{} type identOptionalParamThree struct{} func WithOptionOne(v ...) Option { return option.New(identOptionalParamOne{}, v) } ``` Then you can call the method we described above as ```go obj.Method(m1, m2, WithOptionOne(...), WithOptionTwo(...), WithOptionThree(...)) ``` Options should be parsed in a code that looks somewhat like this ```go func (obj *Object) Method(m1 Type1, m2 Type2, options ...Option) { paramOne := defaultValueParamOne for _, option := range options { switch option.Ident() { case identOptionalParamOne{}: paramOne = option.Value().(...) } } ... } ``` The loop requires a bit of boilerplate, and admittedly, this is the main downside of this module. However, if you think you want use the Option as a Function pattern, please check the FAQ below for rationale. # Simple usage Most of the times all you need to do is to declare the Option type as an alias in your code: ```go package myawesomepkg import "github.com/lestrrat-go/option" type Option = option.Interface ``` Then you can start defining options like they are described in the SYNOPSIS section. # Differentiating Options When you have multiple methods and options, and those options can only be passed to each one the methods, it's hard to see which options should be passed to which method. ```go func WithX() Option { ... } func WithY() Option { ... } // Now, which of WithX/WithY go to which method? func (*Obj) Method1(options ...Option) {} func (*Obj) Method2(options ...Option) {} ``` In this case the easiest way to make it obvious is to put an extra layer around the options so that they have different types ```go type Method1Option interface { Option method1Option() } type method1Option struct { Option } func (*method1Option) method1Option() {} func WithX() Method1Option { return &methodOption{option.New(...)} } func (*Obj) Method1(options ...Method1Option) {} ``` This way the compiler knows if an option can be passed to a given method. # FAQ ## Why aren't these function-based? Using a base option type like `type Option func(ctx interface{})` is certainly one way to achieve the same goal. In this case, you are giving the option itself the ability to "configure" the main object. For example: ```go type Foo struct { optionaValue bool } type Option func(*Foo) error func WithOptionalValue(v bool) Option { return Option(func(f *Foo) error { f.optionalValue = v return nil }) } func NewFoo(options ...Option) (*Foo, error) { var f Foo for _, o := range options { if err := o(&f); err != nil { return nil, err } } return &f } ``` This in itself is fine, but we think there are a few problems: ### 1. It's hard to create a reusable "Option" type We create many libraries using this optional pattern. We would like to provide a default base object. However, this function based approach is not reusuable because each "Option" type requires that it has a context-specific input type. For example, if the "Option" type in the previous example was `func(interface{}) error`, then its usability will significantly decrease because of the type conversion. This is not to say that this library's approach is better as it also requires type conversion to convert the _value_ of the option. However, part of the beauty of the original function based approach was the ease of its use, and we claim that this significantly decreases the merits of the function based approach. ### 2. The receiver requires exported fields Part of the appeal for a function-based option pattern is by giving the option itself the ability to do what it wants, you open up the possibility of allowing third-parties to create options that do things that the library authors did not think about. ```go package thirdparty , but when I read drum sheet music, I kind of get thrown off b/c many times it says to hit the bass drum where I feel like it's a snare hit. func WithMyAwesomeOption( ... ) mypkg.Option { return mypkg.Option(func(f *mypkg) error { f.X = ... f.Y = ... f.Z = ... return nil }) } ``` However, for any third party code to access and set field values, these fields (`X`, `Y`, `Z`) must be exported. Basically you will need an "open" struct. Exported fields are absolutely no problem when you have a struct that represents data alone (i.e., API calls that refer or change state information) happen, but we think that casually expose fields for a library struct is a sure way to maintenance hell in the future. What happens when you want to change the API? What happens when you realize that you want to use the field as state (i.e. use it for more than configuration)? What if they kept referring to that field, and then you have concurrent code accessing it? Giving third parties complete access to exported fields is like handing out a loaded weapon to the users, and you are at their mercy. Of course, providing public APIs for everything so you can validate and control concurrency is an option, but then ... it's a lot of work, and you may have to provide APIs _only_ so that users can refer it in the option-configuration phase. That sounds like a lot of extra work. golang-github-lestrrat-go-option-1.0.1/cmd/000077500000000000000000000000001464252213300205545ustar00rootroot00000000000000golang-github-lestrrat-go-option-1.0.1/cmd/genoptions/000077500000000000000000000000001464252213300227415ustar00rootroot00000000000000golang-github-lestrrat-go-option-1.0.1/cmd/genoptions/go.mod000066400000000000000000000012731464252213300240520ustar00rootroot00000000000000module 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 ) golang-github-lestrrat-go-option-1.0.1/cmd/genoptions/go.sum000066400000000000000000000143121464252213300240750ustar00rootroot00000000000000github.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= golang-github-lestrrat-go-option-1.0.1/cmd/genoptions/main.go000066400000000000000000000135171464252213300242230ustar00rootroot00000000000000// This tools is experimental and is not officially supported. Use at your own peril. 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 } golang-github-lestrrat-go-option-1.0.1/go.mod000066400000000000000000000001321464252213300211130ustar00rootroot00000000000000module github.com/lestrrat-go/option go 1.16 require github.com/stretchr/testify v1.6.1 golang-github-lestrrat-go-option-1.0.1/go.sum000066400000000000000000000020001464252213300211340ustar00rootroot00000000000000github.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/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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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= golang-github-lestrrat-go-option-1.0.1/option.go000066400000000000000000000013231464252213300216470ustar00rootroot00000000000000package option import "fmt" // Interface defines the minimum interface that an option must fulfill type Interface interface { // Ident returns the "identity" of this option, a unique identifier that // can be used to differentiate between options Ident() interface{} // Value returns the corresponding value. Value() interface{} } type pair struct { ident interface{} value interface{} } // New creates a new Option func New(ident, value interface{}) Interface { return &pair{ ident: ident, value: value, } } func (p *pair) Ident() interface{} { return p.ident } func (p *pair) Value() interface{} { return p.value } func (p *pair) String() string { return fmt.Sprintf(`%v(%v)`, p.ident, p.value) } golang-github-lestrrat-go-option-1.0.1/option_test.go000066400000000000000000000012771464252213300227160ustar00rootroot00000000000000package option_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/lestrrat-go/option" ) type identFoo struct{} type identBar struct{} func TestOption(t *testing.T) { options := []option.Interface{ option.New(identFoo{}, "foo"), option.New(identBar{}, 1), } expected := []struct { ident interface{} value interface{} }{ { ident: identFoo{}, value: "foo", }, { ident: identBar{}, value: 1, }, } for i := 0; i < len(options); i++ { if !assert.Equal(t, expected[i].ident, options[i].Ident(), `identities should match`) { return } if !assert.Equal(t, expected[i].value, options[i].Value(), `values should match`) { return } } }