Skip to content

A Go package for using the options pattern

License

Notifications You must be signed in to change notification settings

broothie/option

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

option

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.

Getting Started

Go Get

go get github.com/broothie/option

Import

import "github.com/broothie/option"

Documentation

The API is small, so taking a look at the package index on pkg.go.dev should get you up to speed.

Usage

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"),
)

Implementing Option

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"}
})