feat: options.ApplyTo for default values (#225)

## Why this should be merged

Adds default-value support to the `options` package, which could
previously only create configured types from zero values.

## How this works

Exposes the option-application loop as the `ApplyTo()` function and
refactors `As()` to share this code path.

## How this was tested

Testable example acting as a unit test.
This commit is contained in:
Arran Schlosberg 2025-09-09 15:11:33 +01:00 committed by GitHub
parent 4bc19e9f8f
commit ba9ab538b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 73 additions and 2 deletions

View file

@ -0,0 +1,66 @@
// Copyright 2025 the libevm authors.
//
// The libevm additions to go-ethereum are free software: you can redistribute
// them and/or modify them under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// The libevm additions are distributed in the hope that they will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see
// <http://www.gnu.org/licenses/>.
package options_test
import (
"fmt"
"github.com/ava-labs/libevm/libevm/options"
)
// config is an arbitrary type to be configured with [options.Option] values.
// Although it can be exported, there is typically no need.
type config struct {
num int
flag bool
}
// An Option configures an arbitrary type. Using a type alias (=) instead of a
// completely new type is recommended as it maintains compatibility with helpers
// such as [options.Func].
type Option = options.Option[config]
func WithNum(n int) Option {
return options.Func[config](func(c *config) {
c.num = n
})
}
func WithFlag(b bool) Option {
return options.Func[config](func(c *config) {
c.flag = b
})
}
func Example() {
num42 := WithNum(42)
flagOn := WithFlag(true)
// Some IDEs and linters might complain about the redundant type parameter
// as it can be inferred, but it makes for more readable code.
fromZero := options.As[config](num42, flagOn)
fmt.Printf("From zero value: %T(%+[1]v)\n", fromZero)
fromDefault := options.ApplyTo(&config{
num: 100,
}, flagOn)
fmt.Printf("Applied to default value: %T(%+[1]v)\n", fromDefault)
// Output:
// From zero value: *options_test.config(&{num:42 flag:true})
// Applied to default value: *options_test.config(&{num:100 flag:true})
}

View file

@ -26,10 +26,15 @@ type Option[T any] interface {
// As applies Options to a zero-value T, which it then returns.
func As[T any](opts ...Option[T]) *T {
var t T
return ApplyTo(&t, opts...)
}
// ApplyTo applies Options to the T and returns it for convenience.
func ApplyTo[T any](t *T, opts ...Option[T]) *T {
for _, o := range opts {
o.Configure(&t)
o.Configure(t)
}
return &t
return t
}
// A Func converts a function into an [Option], using itself as the Configure