structconfig

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Dec 29, 2024 License: MIT Imports: 3 Imported by: 7

README

Go Report Card Go Reference

structconfig

Map default values, environment variables, and command-line arguments to struct tags.

Installation

go get github.com/berquerant/structconfig

Examples

Default values

type T struct {
  I int `default:"10"`
}

sc := structconfig.New[T]()
var got T
if err := sc.FromDefault(&got); err != nil {
  panic(err)
}
// got.I == 10

Environment variables

type T struct {
  I int `name:"int_value"`
}

os.Setenv("INT_VALUE", "10")
sc := structconfig.New[T]()
var got T
if err := sc.FromEnv(&got); err != nil {
  panic(err)
}
// got.I == 10

Command-line flags (pflag)

type T struct {
  I int `name:"int_value" default:"10"`
}

var fs *pflag.FlagSet = // ...
sc := structconfig.New[T]()
if err := sc.SetFlags(fs); err != nil {
  panic(err)
}
if err := fs.Parse([]string{"--int_value", "100"}); err != nil {
  panic(err)
}
var got T
if err := sc.FromFlags(&got, fs); err != nil {
  panic(err)
}
// got.I == 100

More examples

Documentation

Index

Examples

Constants

View Source
const (
	TagName    = internal.TagName
	TagUsage   = internal.TagUsage
	TagDefault = internal.TagDefault
	TagShort   = internal.TagShort
)

Variables

View Source
var (
	ErrStructConfig     = internal.ErrStructConfig
	ErrNotStruct        = internal.ErrNotStruct
	ErrNotStructPointer = internal.ErrNotStructPointer
)

Functions

func IsSupportedKind

func IsSupportedKind(k reflect.Kind) bool

Types

type AnyCallbackFunc

type AnyCallbackFunc = func(StructField, string, func() reflect.Value) error

type AnyEqualFunc

type AnyEqualFunc = func(left, right any) (bool, error)

type AnyReceptor

type AnyReceptor = internal.AnyReceptor

type BoolReceptor

type BoolReceptor = internal.BoolReceptor

type Config

type Config struct {
	AnyCallback *ConfigItem[AnyCallbackFunc]
	AnyEqual    *ConfigItem[AnyEqualFunc]
	Prefix      *ConfigItem[string]
}

func (*Config) Apply

func (s *Config) Apply(opt ...Option)

type ConfigBuilder

type ConfigBuilder struct {
	// contains filtered or unexported fields
}

func NewConfigBuilder

func NewConfigBuilder() *ConfigBuilder

func (*ConfigBuilder) AnyCallback

func (s *ConfigBuilder) AnyCallback(v AnyCallbackFunc) *ConfigBuilder

func (*ConfigBuilder) AnyEqual

func (s *ConfigBuilder) AnyEqual(v AnyEqualFunc) *ConfigBuilder

func (*ConfigBuilder) Build

func (s *ConfigBuilder) Build() *Config

func (*ConfigBuilder) Prefix

func (s *ConfigBuilder) Prefix(v string) *ConfigBuilder

type ConfigItem

type ConfigItem[T any] struct {
	// contains filtered or unexported fields
}

func NewConfigItem

func NewConfigItem[T any](defaultValue T) *ConfigItem[T]

func (*ConfigItem[T]) Default

func (s *ConfigItem[T]) Default() T

func (*ConfigItem[T]) Get

func (s *ConfigItem[T]) Get() T

func (*ConfigItem[T]) IsModified

func (s *ConfigItem[T]) IsModified() bool

func (*ConfigItem[T]) Set

func (s *ConfigItem[T]) Set(value T)

type EnvVar

type EnvVar = internal.EnvVar

type FloatReceptor

type FloatReceptor = internal.FloatReceptor

type IntReceptor

type IntReceptor = internal.IntReceptor

type Merger

type Merger[T any] struct {
	*internal.Merger[T]
}
Example
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"

	"github.com/berquerant/structconfig"
)

func main() {
	type T struct {
		I  int    `default:"1"`
		S  string `default:"s"`
		II []int  `default:"[1]"`
	}

	callback := func(s structconfig.StructField, v string, fv func() reflect.Value) error {
		if s.Name() != "II" {
			return errors.New("unexpected field name")
		}
		var xs []int
		if err := json.Unmarshal([]byte(v), &xs); err != nil {
			return err
		}
		fv().Set(reflect.ValueOf(xs))
		return nil
	}

	eq := func(a, b any) (bool, error) {
		// expect only []int because int and string are supported by structconfig
		xs, ok := a.([]int)
		if !ok {
			return false, nil
		}
		ys, ok := b.([]int)
		if !ok {
			return false, nil
		}
		if len(xs) != len(ys) {
			return false, nil
		}
		for i, x := range xs {
			if x != ys[i] {
				return false, nil
			}
		}
		return true, nil
	}

	m := structconfig.NewMerger[T](
		structconfig.WithAnyCallback(callback),
		structconfig.WithAnyEqual(eq),
	)
	got, err := m.Merge(
		T{
			I:  100,
			S:  "s", // default
			II: []int{100},
		},
		T{
			I:  1, // default
			S:  "win",
			II: []int{1}, // default
		},
	)
	if err != nil {
		panic(err)
	}
	fmt.Println(got.I, got.S, got.II)
}
Output:

100 win [100]

func NewMerger

func NewMerger[T any](opt ...Option) *Merger[T]

NewMerger returns a new Merger.

AnyCallback parses "default" tag value and set it. AnyEqual reports true if left equals right when kind of arguments are not supported. Prefix adds a prefix to "default" tag name.

func (*Merger[T]) Merge

func (m *Merger[T]) Merge(left, right T) (T, error)

Merge values based on the 'default' tag values. For each field, if the right value is not the default, use it; if not, use the left value. If that is also the default, set the default value. Return this instance.

type Option

type Option func(*Config)

func WithAnyCallback

func WithAnyCallback(v AnyCallbackFunc) Option

func WithAnyEqual

func WithAnyEqual(v AnyEqualFunc) Option

func WithPrefix

func WithPrefix(v string) Option

type Receptor

type Receptor = internal.Receptor

type StringReceptor

type StringReceptor = internal.StringReceptor

type StructConfig

type StructConfig[T any] struct {
	// contains filtered or unexported fields
}

func New

func New[T any](opt ...Option) *StructConfig[T]

New returns a new StructConfig.

AnyCallback parses "default" tag value and set it. Prefix adds a prefix to "name", "short", "default" and "usage" tag name.

func (StructConfig[T]) FromDefault

func (sc StructConfig[T]) FromDefault(v *T) error

FromDefault sets "default" tag values to v.

Example
package main

import (
	"fmt"

	"github.com/berquerant/structconfig"
)

func main() {
	type T struct {
		B bool `default:"false"`
		I int
		F float32 `default:"1.1"`
		S string  `default:"str"`
	}

	sc := structconfig.New[T]()
	var got T
	if err := sc.FromDefault(&got); err != nil {
		panic(err)
	}
	fmt.Println(got.B, got.I, got.F, got.S)
}
Output:

false 0 1.1 str

func (StructConfig[T]) FromEnv

func (sc StructConfig[T]) FromEnv(v *T) error

FromEnv sets environment variable values to v.

Environment variable name will be

NewEnvVar("name tag value").String()

All '.' and '-' will be replaced with '_', making it all uppsercase.

Example
package main

import (
	"fmt"
	"os"

	"github.com/berquerant/structconfig"
)

func main() {
	type T struct {
		B  bool   `name:"bool_value"`
		S  string `name:"string_value"`
		N  int    `name:"int_value" default:"10"`
		N2 int
		N3 int `name:"-"`
	}

	envs := map[string]string{
		"BOOL_VALUE":   "true",
		"STRING_VALUE": "str",
	}
	for k, v := range envs {
		os.Setenv(k, v)
	}
	defer func() {
		for k := range envs {
			os.Unsetenv(k)
		}
	}()

	sc := structconfig.New[T]()
	var got T
	if err := sc.FromEnv(&got); err != nil {
		panic(err)
	}
	fmt.Println(got.B, got.S, got.N, got.N2, got.N3)
}
Output:

true str 10 0 0

func (StructConfig[T]) FromFlags

func (sc StructConfig[T]) FromFlags(v *T, fs *pflag.FlagSet) error

FromFlags sets values to v from command-line flags.

Flag name is from "name" tag value.

Example
package main

import (
	"errors"
	"fmt"
	"reflect"
	"sort"
	"strings"

	"github.com/berquerant/structconfig"
	"github.com/spf13/pflag"
)

func main() {
	type T struct {
		B       bool   `name:"bool_value" usage:"BOOL"`
		S       string `name:"string_value" default:"str"`
		X       bool   `name:"bool_short" short:"x"`
		Ignore1 int
		Ignore2 int `name:"-"`
		V       struct {
			S string
		} `name:"struct_value"`
	}

	anyCallback := func(s structconfig.StructField, v string, fv func() reflect.Value) error {
		if x, ok := s.Tag().Name(); !ok || x != "struct_value" {
			// among T, only struct_value is not supported
			return errors.New("unexpected tag name")
		}
		fv().Set(reflect.ValueOf(struct {
			S string
		}{
			S: v,
		}))
		return nil
	}

	fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
	sc := structconfig.New[T](structconfig.WithAnyCallback(anyCallback))

	if err := sc.SetFlags(fs); err != nil {
		panic(err)
	}

	flagNames := []string{}
	fs.VisitAll(func(f *pflag.Flag) {
		flagNames = append(flagNames, f.Name)
	})

	if err := fs.Parse([]string{"--bool_value", "--struct_value", "sv", "-x"}); err != nil {
		panic(err)
	}

	var got T
	if err := sc.FromFlags(&got, fs); err != nil {
		panic(err)
	}

	sort.Strings(flagNames)
	fmt.Println(strings.Join(flagNames, ","))
	fmt.Println(got.B, got.S, got.Ignore1, got.Ignore2, got.V.S, got.X)
}
Output:

bool_short,bool_value,string_value,struct_value
true str 0 0 sv true
Example (Prefix)
package main

import (
	"fmt"
	"sort"
	"strings"

	"github.com/berquerant/structconfig"
	"github.com/spf13/pflag"
)

func main() {
	tagPrefix := "sc"
	type T struct {
		B bool   `scname:"bool_value" scusage:"BOOL"`
		S string `scname:"string_value" scdefault:"str"`
	}

	fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
	sc := structconfig.New[T](structconfig.WithPrefix(tagPrefix))

	if err := sc.SetFlags(fs); err != nil {
		panic(err)
	}

	flagNames := []string{}
	fs.VisitAll(func(f *pflag.Flag) {
		flagNames = append(flagNames, f.Name)
	})

	if err := fs.Parse([]string{"--bool_value", "--string_value", "sv"}); err != nil {
		panic(err)
	}

	var got T
	if err := sc.FromFlags(&got, fs); err != nil {
		panic(err)
	}

	sort.Strings(flagNames)
	fmt.Println(strings.Join(flagNames, ","))
	fmt.Println(got.B, got.S)
}
Output:

bool_value,string_value
true sv

func (StructConfig[T]) SetFlags

func (sc StructConfig[T]) SetFlags(fs *pflag.FlagSet) error

SetFlags sets command-line flags.

Flag name is from "name" tag value. Flag shorthand is from "short" tag value. Flag default value is from "default" tag value. Flag usage is from "usage" tag value.

type StructField

type StructField = internal.StructField

type Supported

type Supported = internal.Supported

type Tag

type Tag = internal.Tag

type Type

type Type = internal.Type

func NewType

func NewType(v any, prefix string) (*Type, error)

type UintReceptor

type UintReceptor = internal.UintReceptor

type Unsigned

type Unsigned = internal.Unsigned

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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