commit
b985c9ff0b
|
@ -0,0 +1 @@
|
||||||
|
.idea/
|
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2022 John Olheiser
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,38 @@
|
||||||
|
# opt
|
||||||
|
|
||||||
|
`opt` is a tiny package that reduces boilerplate for functional options.
|
||||||
|
|
||||||
|
Simply decide whether your options can return errors or not, then use the corresponding type.
|
||||||
|
|
||||||
|
Without errors:
|
||||||
|
```go
|
||||||
|
type Foo struct {
|
||||||
|
Bar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithBar(b string) opt.Func[Foo] {
|
||||||
|
return func(f *Foo) {
|
||||||
|
f.Bar = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With errors:
|
||||||
|
```go
|
||||||
|
type Foo struct {
|
||||||
|
Bar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithBar(b string) opt.ErrorFunc[Foo] {
|
||||||
|
return func(f *Foo) error {
|
||||||
|
f.Bar = b
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
Credit to `@segfaultax` in the [Discord Gophers](https://discord.gg/golang) server for the idea and starting implementation.
|
|
@ -0,0 +1,5 @@
|
||||||
|
module go.jolheiser.com/opt
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require github.com/matryer/is v1.4.0
|
|
@ -0,0 +1,2 @@
|
||||||
|
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||||
|
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
|
@ -0,0 +1,24 @@
|
||||||
|
package opt
|
||||||
|
|
||||||
|
// Func is a functional option for A
|
||||||
|
type Func[A any] func(*A)
|
||||||
|
|
||||||
|
// Apply applies all options to A
|
||||||
|
func Apply[A any](a *A, opts ...Func[A]) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorFunc is a functional option for A that can return an error
|
||||||
|
type ErrorFunc[A any] func(*A) error
|
||||||
|
|
||||||
|
// ApplyError applies all options to A, returning the first error
|
||||||
|
func ApplyError[A any](a *A, opts ...ErrorFunc[A]) error {
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(a); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package opt_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.jolheiser.com/opt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleApply() {
|
||||||
|
f := &Foo{
|
||||||
|
Bar: "default",
|
||||||
|
}
|
||||||
|
opt.Apply(f, WithBar("override"))
|
||||||
|
|
||||||
|
fmt.Println(f.Bar)
|
||||||
|
// Output:
|
||||||
|
// override
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleApplyError() {
|
||||||
|
f := &Foo{
|
||||||
|
Bar: "default",
|
||||||
|
}
|
||||||
|
err := opt.ApplyError(f, WithBarErr("uh oh!")) // WithBarErr always returns errors.New(b)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
// Output:
|
||||||
|
// uh oh!
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package opt_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.jolheiser.com/opt"
|
||||||
|
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOptions(t *testing.T) {
|
||||||
|
assert := is.New(t)
|
||||||
|
|
||||||
|
f := NewFoo(WithBar("bar"))
|
||||||
|
assert.Equal(f.Bar, "bar") // Option should set bar
|
||||||
|
|
||||||
|
opt.Apply(f, WithBaz(100))
|
||||||
|
assert.Equal(f.Baz, 100) // Apply should set Baz
|
||||||
|
|
||||||
|
_, err := NewErrorFoo(WithBarErr("bar"))
|
||||||
|
assert.True(err != nil) // ErrorOption should return error
|
||||||
|
assert.Equal(err.Error(), "bar") // ErrorOption error should be bar
|
||||||
|
|
||||||
|
err = opt.ApplyError(f, WithBazErr(100))
|
||||||
|
assert.True(err != nil) // ApplyError should return error
|
||||||
|
assert.Equal(err.Error(), "100") // ApplyError error should be 100
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
Bar string
|
||||||
|
Baz int
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithBar(b string) opt.Func[Foo] {
|
||||||
|
return func(f *Foo) {
|
||||||
|
f.Bar = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithBaz(b int) opt.Func[Foo] {
|
||||||
|
return func(f *Foo) {
|
||||||
|
f.Baz = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFoo(opts ...opt.Func[Foo]) *Foo {
|
||||||
|
f := &Foo{ /** some defaults **/ }
|
||||||
|
opt.Apply(f, opts...)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrorFoo(opts ...opt.ErrorFunc[Foo]) (*Foo, error) {
|
||||||
|
f := &Foo{ /** some defaults **/ }
|
||||||
|
return f, opt.ApplyError(f, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithBarErr(b string) opt.ErrorFunc[Foo] {
|
||||||
|
return func(f *Foo) error {
|
||||||
|
return errors.New(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithBazErr(b int) opt.ErrorFunc[Foo] {
|
||||||
|
return func(f *Foo) error {
|
||||||
|
return errors.New(strconv.Itoa(b))
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue