opt

package
v0.17.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 31, 2026 License: Apache-2.0, Apache-2.0 Imports: 0 Imported by: 0

README

opt

A type-safe, generic options pattern implementation for Go that provides a clean and flexible way to configure objects using functional options.

Features

  • Type-safe: Uses Go generics for compile-time type safety
  • Flexible: Works with any type (structs, interfaces, primitives)
  • Composable: Options can be combined and applied in sequence
  • Error handling: Built-in error handling for validation and configuration failures
  • Zero dependencies: Pure Go implementation with no external dependencies

Installation

go get -u devnw.dev/opt@latest

Quick Start

package main

import (
    "fmt"
    "log"

    "devnw.dev/opt"
)

// Define your configuration struct
type ServerConfig struct {
    Host    string
    Port    int
    Timeout time.Duration
    Debug   bool
}

// Create option functions
func WithHost(host string) opt.Option[*ServerConfig] {
    return func(config *ServerConfig) error {
        if host == "" {
            return errors.New("host cannot be empty")
        }
        config.Host = host
        return nil
    }
}

func WithPort(port int) opt.Option[*ServerConfig] {
    return func(config *ServerConfig) error {
        if port <= 0 || port > 65535 {
            return errors.New("invalid port range")
        }
        config.Port = port
        return nil
    }
}

func WithTimeout(timeout time.Duration) opt.Option[*ServerConfig] {
    return func(config *ServerConfig) error {
        config.Timeout = timeout
        return nil
    }
}

func WithDebug(debug bool) opt.Option[*ServerConfig] {
    return func(config *ServerConfig) error {
        config.Debug = debug
        return nil
    }
}

// Use the options
func main() {
    config := &ServerConfig{
        Host: "localhost", // default values
        Port: 8080,
    }

    // Apply options
    options := opt.Options[*ServerConfig]{
        WithHost("api.example.com"),
        WithPort(443),
        WithTimeout(30 * time.Second),
        WithDebug(true),
    }

    if err := options.Apply(config); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Server config: %+v\n", config)
}

API Reference

Types
Option[T any]
type Option[T any] func(T) error

A functional option that takes a value of type T and returns an error if the option application fails.

Methods:

  • Apply(v T) error - Applies the option to the given value
Options[T any]
type Options[T any] []Option[T]

A slice of options that can be applied sequentially to a value.

Methods:

  • Apply(v T) error - Applies all options in sequence, stopping on the first error

Usage Patterns

Constructor with Options
func NewServer(opts ...opt.Option[*ServerConfig]) (*Server, error) {
    config := &ServerConfig{
        Host: "localhost",
        Port: 8080,
        Timeout: 10 * time.Second,
    }

    options := opt.Options[*ServerConfig](opts)
    if err := options.Apply(config); err != nil {
        return nil, err
    }

    return &Server{config: config}, nil
}

// Usage
server, err := NewServer(
    WithHost("api.example.com"),
    WithPort(443),
    WithDebug(true),
)
Method Chaining Alternative
type ConfigBuilder struct {
    config *ServerConfig
}

func NewConfigBuilder() *ConfigBuilder {
    return &ConfigBuilder{
        config: &ServerConfig{
            Host: "localhost",
            Port: 8080,
        },
    }
}

func (cb *ConfigBuilder) Apply(opts ...opt.Option[*ServerConfig]) error {
    options := opt.Options[*ServerConfig](opts)
    return options.Apply(cb.config)
}

func (cb *ConfigBuilder) Build() *ServerConfig {
    return cb.config
}

// Usage
config, err := NewConfigBuilder().
    Apply(WithHost("api.example.com")).
    Apply(WithPort(443)).
    Build()
Conditional Options
func conditionalOptions(isDev bool) []opt.Option[*ServerConfig] {
    var opts []opt.Option[*ServerConfig]

    if isDev {
        opts = append(opts, WithDebug(true))
        opts = append(opts, WithHost("localhost"))
    } else {
        opts = append(opts, WithDebug(false))
        opts = append(opts, WithHost("prod.example.com"))
    }

    return opts
}

// Usage
options := opt.Options[*ServerConfig](conditionalOptions(true))

Error Handling

Options support comprehensive error handling. When an error occurs during option application:

  1. Single Option: Returns the error immediately
  2. Multiple Options: Stops at the first error and returns it
  3. Partial Application: Previously applied options remain in effect
config := &ServerConfig{}
options := opt.Options[*ServerConfig]{
    WithHost("valid-host"),      // ✓ Applied successfully
    WithPort(-1),                // ✗ Error: invalid port
    WithDebug(true),             // ✗ Not reached due to previous error
}

err := options.Apply(config)
// err != nil
// config.Host == "valid-host" (partial application)
// config.Port == 0 (unchanged due to error)

Best Practices

1. Validation in Options

Always validate input in option functions:

func WithPort(port int) opt.Option[*ServerConfig] {
    return func(config *ServerConfig) error {
        if port <= 0 || port > 65535 {
            return fmt.Errorf("invalid port %d: must be between 1 and 65535", port)
        }
        config.Port = port
        return nil
    }
}
2. Use Pointer Types

Use pointer types for the target of options to ensure modifications persist:

// ✓ Good - modifications persist
type Option[T any] func(*T) error

// ✗ Avoid - modifications lost due to value copying
type Option[T any] func(T) error
3. Provide Default Values

Set sensible defaults before applying options:

func NewServer(opts ...opt.Option[*ServerConfig]) *Server {
    config := &ServerConfig{
        Host:    "localhost",  // Default values
        Port:    8080,
        Timeout: 30 * time.Second,
        Debug:   false,
    }

    // Apply user options
    options := opt.Options[*ServerConfig](opts)
    options.Apply(config)

    return &Server{config: config}
}
4. Compose Options

Create higher-level options by composing simpler ones:

func WithProductionDefaults() opt.Option[*ServerConfig] {
    return func(config *ServerConfig) error {
        prodOptions := opt.Options[*ServerConfig]{
            WithHost("prod.example.com"),
            WithPort(443),
            WithTimeout(60 * time.Second),
            WithDebug(false),
        }
        return prodOptions.Apply(config)
    }
}

Testing

The package includes comprehensive tests demonstrating usage patterns and edge cases. Run tests with:

go test -v ./...

For coverage analysis:

go test -cover ./...

Contributing

This project follows standard Go conventions. Please ensure:

  1. All code is properly formatted (go fmt)
  2. All tests pass (go test ./...)
  3. Code coverage remains high
  4. Documentation is updated for new features

License

See LICENSE file for details.

Generic Makefile

This project includes a generic, reusable Makefile template for Go projects. The template provides:

  • Standardized workflow: Consistent targets across all Go projects
  • Configurable: Easy customization through variables
  • CI/CD ready: Separate targets for continuous integration
  • Comprehensive: Covers development, testing, building, and releasing
  • Self-documenting: Built-in help and info targets
Quick Setup for New Projects
# Bootstrap the generic Makefile in your Go project
curl -fsSL https://raw.githubusercontent.com/codeprosorg/opt/main/scripts/bootstrap-makefile.sh | bash

# Or manually download the template
curl -fsSL https://raw.githubusercontent.com/codeprosorg/opt/main/Makefile.template > Makefile
Common Commands
make help          # Show all available targets
make all           # Build, test, and verify project
make test-coverage # Run tests with coverage report
make info          # Show project information

See MAKEFILE.md for complete documentation.

Documentation

Overview

Package opt provides a small generic implementation of the functional options pattern. Option[T] is a function that mutates a value of type T and may return an error; Options[T] is a slice of them whose Apply method runs each option in order and stops at the first error. It lets constructors accept a variadic ...Option[T] for type-safe, composable configuration without per-type boilerplate.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option

type Option[T any] func(T) error

func (Option[T]) Apply

func (o Option[T]) Apply(v T) error

type Options

type Options[T any] []Option[T]

func (Options[T]) Apply

func (o Options[T]) Apply(v T) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL