Go Reference Go Report Card codecov gosec
This package aims to make it easy to use the options pattern in Go. It uses generics to ensure type safety, and has no dependencies outside the standard library.
go get github.com/broothie/option
import "github.com/broothie/option"
The API is small, so taking a look at the package index on pkg.go.dev should get you up to speed.
Let's say you have a Server that you'd like to be configurable:
package server import ( "database/sql" "log/slog" ) type Server struct { logger *slog.Logger db *sql.DB }
First, provide callers with option builders:
func Logger(logger *slog.Logger) option.Func[*Server] { return func(server *Server) (*Server, error) { server.logger = logger return server, nil } } func DB(name string) option.Func[*Server] { return func(server *Server) (*Server, error) { db, err := sql.Open("pg", name) if err != nil { return nil, err } server.db = db return server, nil } }
Then define a constructor that accepts a variadic argument of type option.Option[*Server].
In it, use option.Apply to run the new server instance through the provided options.
func New(options ...option.Option[*Server]) (*Server, error) { return option.Apply(new(Server), options...) }
Now, callers can use the options pattern when instantiating your server:
srv, err := server.New( server.Logger(slog.New(slog.NewTextHandler(os.Stdout, nil))), server.DB("some-connection-string"), )
Let's say you want your server to always respond with a set of HTTP headers:
type Server struct { headers http.Header }
You can create a custom Option for configuring this headers like this:
type Headers http.Header func (h Headers) Apply(server *Server) (*Server, error) { server.headers = http.Header(h) return server, nil }
Now, a caller can configure their server's headers like this:
srv, err := server.New(server.Headers{ "Content-Type": {"application/json"}, "X-From": {"my-app"} })