Skip to content

Commit

Permalink
refactor(konfetty)!: simplify naming of some exported types and
Browse files Browse the repository at this point in the history
functions
  • Loading branch information
nikoksr committed Aug 9, 2024
1 parent 6f96bea commit 8709ea5
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 63 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func main() {
},
}

cfg, err := konfetty.FromConfig(cfg).
cfg, err := konfetty.FromStruct(cfg).
WithDefaults(
// Devices are disabled by default
BaseDevice{Enabled: false},
Expand Down Expand Up @@ -151,7 +151,7 @@ Konfetty doesn't replace your current config loading mechanism — it enhances i
viper.ReadInConfig()
viper.Unmarshal(&config)

config, err := konfetty.FromConfig(&config).
config, err := konfetty.FromStruct(&config).
WithDefaults(defaultConfig).
WithTransformer(transformer).
WithValidator(validator).
Expand All @@ -165,7 +165,7 @@ k := koanf.New(".")
k.Load(file.Provider("config.yaml"), yaml.Parser())
k.Unmarshal("", &config)

config, err := konfetty.FromConfig(&config).
config, err := konfetty.FromStruct(&config).
WithDefaults(defaultConfig).
Build()
```
Expand Down
99 changes: 46 additions & 53 deletions konfetty.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Package konfetty provides a flexible, type-safe configuration processing system.
// Package konfetty provides zero-dependency, type-safe and powerful post-processing for your data structs,
// mostly focused on applying defaults, transformations, and validations to configuration structures.
package konfetty

import (
Expand All @@ -7,75 +8,71 @@ import (
"reflect"
)

// ConfigProvider defines an interface for loading configurations.
type ConfigProvider[T any] interface {
// Provider defines an interface for loading structured data.
type Provider[T any] interface {
Load() (T, error)
}

// configSource is an internal type to represent the source of configuration.
type configSource[T any] struct {
config *T
// dataSource is an internal type to represent the source of data.
type dataSource[T any] struct {
data *T
loaderFunc func() (T, error)
provider ConfigProvider[T]
provider Provider[T]
}

// ConfigBuilder orchestrates the configuration building process. It manages the configuration source, defaults,
// transformations, and validations.
type ConfigBuilder[T any] struct {
source configSource[T]
// Builder orchestrates the building process. It manages the data source, defaults, transformations, and validations.
type Builder[T any] struct {
source dataSource[T]
defaults map[reflect.Type][]any
transform func(*T)
validate func(*T) error
}

// ConfigProcessor exposes methods for further configuration processing. It wraps a ConfigBuilder and provides a fluent
// interface for configuration setup.
type ConfigProcessor[T any] struct {
builder *ConfigBuilder[T]
// Processor exposes methods for further data-structure processing. It wraps a Builder and provides a fluent interface
// for configuration setup.
type Processor[T any] struct {
builder *Builder[T]
}

// FromConfig initializes a ConfigProcessor with a pre-loaded configuration. Use this when you already have a populated
// config struct:
// FromStruct initializes a Processor with a pre-populated struct.
//
// cfg := &MyConfig{...}
// processor := konfetty.FromConfig(cfg)
func FromConfig[T any](config *T) *ConfigProcessor[T] {
return &ConfigProcessor[T]{
builder: &ConfigBuilder[T]{
source: configSource[T]{config: config},
// processor := konfetty.FromStruct(cfg)
func FromStruct[T any](config *T) *Processor[T] {
return &Processor[T]{
builder: &Builder[T]{
source: dataSource[T]{data: config},
},
}
}

// FromLoaderFunc initializes a ConfigProcessor with a function that loads the configuration. Use this when you have a
// custom loading function:
// FromLoaderFunc initializes a Processor with a function that loads the data-structure.
//
// loader := func() (MyConfig, error) { ... }
// processor := konfetty.FromLoaderFunc(loader)
func FromLoaderFunc[T any](loader func() (T, error)) *ConfigProcessor[T] {
return &ConfigProcessor[T]{
builder: &ConfigBuilder[T]{
source: configSource[T]{loaderFunc: loader},
func FromLoaderFunc[T any](loader func() (T, error)) *Processor[T] {
return &Processor[T]{
builder: &Builder[T]{
source: dataSource[T]{loaderFunc: loader},
},
}
}

// FromProvider initializes a ConfigProcessor with a ConfigProvider. Use this when you have an implementation of the
// ConfigProvider interface:
// FromProvider initializes a Processor with a Provider.
//
// provider := MyConfigProvider{}
// processor := konfetty.FromProvider(provider)
func FromProvider[T any](provider ConfigProvider[T]) *ConfigProcessor[T] {
return &ConfigProcessor[T]{
builder: &ConfigBuilder[T]{
source: configSource[T]{provider: provider},
func FromProvider[T any](provider Provider[T]) *Processor[T] {
return &Processor[T]{
builder: &Builder[T]{
source: dataSource[T]{provider: provider},
},
}
}

// WithDefaults adds default values to the configuration processing pipeline. Multiple defaults can be provided and will
// be applied in order.
func (p *ConfigProcessor[T]) WithDefaults(defaultValues ...any) *ConfigProcessor[T] {
// WithDefaults adds default values to the processing pipeline. Multiple defaults can be provided and will be applied
// in order.
func (p *Processor[T]) WithDefaults(defaultValues ...any) *Processor[T] {
if p.builder.defaults == nil {
p.builder.defaults = make(map[reflect.Type][]any)
}
Expand All @@ -88,42 +85,38 @@ func (p *ConfigProcessor[T]) WithDefaults(defaultValues ...any) *ConfigProcessor
return p
}

// WithTransformer sets a custom transformation function to be applied to the configuration.
func (p *ConfigProcessor[T]) WithTransformer(fn func(*T)) *ConfigProcessor[T] {
// WithTransformer sets a custom transformation function to be applied to the data-structure.
func (p *Processor[T]) WithTransformer(fn func(*T)) *Processor[T] {
p.builder.transform = fn
return p
}

// WithValidator sets a custom validation function to be applied to the configuration.
func (p *ConfigProcessor[T]) WithValidator(fn func(*T) error) *ConfigProcessor[T] {
// WithValidator sets a custom validation function to be applied to the data-structure.
func (p *Processor[T]) WithValidator(fn func(*T) error) *Processor[T] {
p.builder.validate = fn
return p
}

// Build processes the configuration, applying defaults, transformations, and validations. It returns the final
// configuration or an error if any step fails.
func (p *ConfigProcessor[T]) Build() (*T, error) {
// Build processes the data-structure, applying defaults, transformations, and validations. It returns the final
// struct or an error if any step fails.
func (p *Processor[T]) Build() (*T, error) {
return p.builder.build()
}

func (b *ConfigBuilder[T]) build() (*T, error) {
// Load
func (b *Builder[T]) build() (*T, error) {
cfg, err := b.load()
if err != nil {
return nil, fmt.Errorf("load: %w", err)
}

// Apply defaults
if err = applyDefaults(&cfg, b.defaults); err != nil {
return nil, fmt.Errorf("apply defaults: %w", err)
}

// Transform
if b.transform != nil {
b.transform(&cfg)
}

// Validate
if b.validate != nil {
if err = b.validate(&cfg); err != nil {
return nil, fmt.Errorf("validate: %w", err)
Expand All @@ -133,13 +126,13 @@ func (b *ConfigBuilder[T]) build() (*T, error) {
return &cfg, nil
}

func (b *ConfigBuilder[T]) load() (T, error) {
func (b *Builder[T]) load() (T, error) {
var cfg T
var err error

switch {
case b.source.config != nil:
cfg = *b.source.config
case b.source.data != nil:
cfg = *b.source.data
case b.source.loaderFunc != nil:
cfg, err = b.source.loaderFunc()
if err != nil {
Expand All @@ -151,7 +144,7 @@ func (b *ConfigBuilder[T]) load() (T, error) {
return cfg, fmt.Errorf("from provider: %w", err)
}
default:
return cfg, errors.New("no configuration source provided")
return cfg, errors.New("no data source provided")
}

return cfg, nil
Expand Down
14 changes: 7 additions & 7 deletions konfetty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ type TestConfig struct {
IsAdmin bool
}

func TestFromConfig(t *testing.T) {
func TestFromStruct(t *testing.T) {
t.Parallel()

config := &TestConfig{Name: "Alice", Age: 30, IsAdmin: true}
processor := konfetty.FromConfig(config)
processor := konfetty.FromStruct(config)

result, err := processor.Build()
require.NoError(t, err)
Expand Down Expand Up @@ -64,7 +64,7 @@ func TestWithDefaults(t *testing.T) {
t.Parallel()

config := &TestConfig{}
processor := konfetty.FromConfig(config).WithDefaults(TestConfig{Name: "Default", Age: 18, IsAdmin: false})
processor := konfetty.FromStruct(config).WithDefaults(TestConfig{Name: "Default", Age: 18, IsAdmin: false})

result, err := processor.Build()
require.NoError(t, err)
Expand All @@ -79,7 +79,7 @@ func TestWithTransformer(t *testing.T) {
c.Name = "Mr. " + c.Name
c.Age++
}
processor := konfetty.FromConfig(config).WithTransformer(transformer)
processor := konfetty.FromStruct(config).WithTransformer(transformer)

result, err := processor.Build()
require.NoError(t, err)
Expand All @@ -96,7 +96,7 @@ func TestWithValidator(t *testing.T) {
}
return nil
}
processor := konfetty.FromConfig(config).WithValidator(validator)
processor := konfetty.FromStruct(config).WithValidator(validator)

_, err := processor.Build()
require.Error(t, err)
Expand All @@ -107,7 +107,7 @@ func TestBuildWithAllOptions(t *testing.T) {
t.Parallel()

config := &TestConfig{}
processor := konfetty.FromConfig(config).
processor := konfetty.FromStruct(config).
WithDefaults(TestConfig{Name: "Default", Age: 18, IsAdmin: false}).
WithTransformer(func(c *TestConfig) {
c.Name = "Mr. " + c.Name
Expand Down Expand Up @@ -158,7 +158,7 @@ func TestBuildErrorCases(t *testing.T) {
validator := func(_ *TestConfig) error {
return errors.New("validator error")
}
processor := konfetty.FromConfig(config).WithValidator(validator)
processor := konfetty.FromStruct(config).WithValidator(validator)

_, err := processor.Build()
require.Error(t, err)
Expand Down

0 comments on commit 8709ea5

Please sign in to comment.