Skip to content

Commit

Permalink
refine go doc
Browse files Browse the repository at this point in the history
  • Loading branch information
ktong committed Dec 15, 2023
1 parent 48b081b commit 2800ed6
Show file tree
Hide file tree
Showing 18 changed files with 169 additions and 153 deletions.
15 changes: 7 additions & 8 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (

// Config reads configuration from appropriate sources.
//
// To create a new Logger, call [New].
// To create a new Config, call [New].
type Config struct {
decodeHook mapstructure.DecodeHookFunc
delimiter string
Expand Down Expand Up @@ -59,9 +59,8 @@ func New(opts ...Option) *Config {
return (*Config)(option)
}

// Load loads configuration from given loaders.
// Each loader takes precedence over the loaders before it
// while multiple loaders are specified.
// Load loads configuration from the given loaders.
// Each loader takes precedence over the loaders before it.
//
// This method can be called multiple times but it is not concurrency-safe.
func (c *Config) Load(loaders ...Loader) error {
Expand Down Expand Up @@ -216,8 +215,8 @@ func sub(values map[string]any, path string, delimiter string) any {
return next
}

// OnChange executes the given onChange function
// while the value of any given path have been changed.
// OnChange registers a callback function that is executed
// when the value of any given path in the default Config changes.
// It requires Config.Watch has been called first.
// The paths are case-insensitive.
//
Expand All @@ -236,8 +235,8 @@ func (c *Config) OnChange(onchange func(*Config), paths ...string) {
}
}

// Unmarshal reads configuration under the given path
// into the given object pointed to by target.
// Unmarshal reads configuration under the given path from the default Config
// and decodes it into the given object pointed to by target.
// The path is case-insensitive.
func (c *Config) Unmarshal(path string, target any) error {
decoder, err := mapstructure.NewDecoder(
Expand Down
18 changes: 10 additions & 8 deletions default.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"github.com/ktong/konf/provider/env"
)

// Get returns the value under the given path.
// It returns zero value if there is an error.
// Get retrieves the value under the given path from the default Config.
// It returns the zero value of the expected type if there is an error.
// The path is case-insensitive.
func Get[T any](path string) T { //nolint:ireturn
var value T
Expand All @@ -28,15 +28,15 @@ func Get[T any](path string) T { //nolint:ireturn
return value
}

// Unmarshal reads configuration under the given path
// into the given object pointed to by target.
// Unmarshal reads configuration under the given path from the default Config
// and decodes it into the given object pointed to by target.
// The path is case-insensitive.
func Unmarshal(path string, target any) error {
return defaultConfig.Load().Unmarshal(path, target)
}

// OnChange executes the given onChange function
// while the value of any given path have been changed.
// OnChange registers a callback function that is executed
// when the value of any given path in the default Config changes.
// It requires Config.Watch has been called first.
// The paths are case-insensitive.
//
Expand All @@ -45,9 +45,11 @@ func OnChange(onChange func(), paths ...string) {
defaultConfig.Load().OnChange(func(*Config) { onChange() }, paths...)
}

// SetDefault makes c the default [Config].
// SetDefault sets the given Config as the default Config.
// After this call, the konf package's top functions (e.g. konf.Get)
// will read from the default config.
// will interact with the given Config.
//
// The default Config loads configuration from environment variables only.
func SetDefault(c *Config) {
defaultConfig.Store(c)
}
Expand Down
2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT license found in the LICENSE file.

/*
Package konf defines a general-purpose configuration API and abstract interfaces
Package konf provides a general-purpose configuration API and abstract interfaces
to back that API. Packages in the Go ecosystem can depend on this package,
while callers can load configuration from whatever source is appropriate.
Expand Down
14 changes: 10 additions & 4 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,38 @@ package konf

import "github.com/mitchellh/mapstructure"

// WithDelimiter provides the delimiter when specifying config path.
// WithDelimiter provides the delimiter used when specifying config paths.
// The delimiter is used to separate keys in the path.
//
// The default delimiter is `.`, which makes config path like `parent.child.key`.
// For example, with the default delimiter `.`, a config path might look like `parent.child.key`.
func WithDelimiter(delimiter string) Option {
return func(options *options) {
options.delimiter = delimiter
}
}

// WithTagName provides the tag name that [mapstructure] reads for field names.
// The tag name is used by mapstructure when decoding configuration into structs.
//
// The default tag name is `konf`.
// For example, with the default tag name `konf`, mapstructure would look for `konf` tags on struct fields.
func WithTagName(tagName string) Option {
return func(options *options) {
options.tagName = tagName
}
}

// WithDecodeHook provides the decode hook for [mapstructure] decoding.
// The decode hook is a function that can transform or customize how values are decoded.
//
// By default, it composes mapstructure.StringToTimeDurationHookFunc,
// mapstructure.StringToSliceHookFunc(",") and mapstructure.TextUnmarshallerHookFunc.
func WithDecodeHook(decodeHook mapstructure.DecodeHookFunc) Option {
return func(options *options) {
options.decodeHook = decodeHook
}
}

// Option configures the given Config.
// Option configures a Config with specific options.
type Option func(*options)

type options Config
8 changes: 4 additions & 4 deletions provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import "context"

// Loader is the interface that wraps the Load method.
//
// Load loads latest configuration and returns as a nested map[string]any.
// It requires that the string keys should be nested like `{parent: {child: {key: 1}}}`.
// The key in returned map should be case-insensitive, otherwise random overridden exists.
// Load loads the latest configuration and returns it as a nested map[string]any.
// The keys in the returned map should be case-insensitive to avoid random overriding.
// The keys should be nested like `{parent: {child: {key: 1}}}`.
type Loader interface {
Load() (map[string]any, error)
}

// Watcher is the interface that wraps the Watch method.
//
// Watch watches configuration and triggers onChange callback with latest
// Watch watches the configuration and triggers the onChange callback with the latest
// full configurations as a nested map[string]any when it changes.
// It blocks until ctx is done, or the watching returns an error.
type Watcher interface {
Expand Down
14 changes: 6 additions & 8 deletions provider/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@

// Package env loads configuration from environment variables.
//
// Env loads all environment variables and returns nested map[string]any.
// by splitting the names by `_`. E.g. the environment variable
// `PARENT_CHILD_KEY="1"` is loaded as `{PARENT: {CHILD: {KEY: "1"}}}`.
// The environment variables with empty value are treated as unset.
// Env loads environment variables whose names starts with the given prefix
// and returns them as a nested map[string]any.
// Environment variables with empty values are treated as unset.
//
// The default behavior can be changed with following options:
// - WithPrefix enables loads environment variables with the given prefix in the name.
// - WithDelimiter provides the delimiter when splitting environment variable name to nested keys.
// It splits the names by delimiter. For example, with the default delimiter "_",
// the environment variable `PARENT_CHILD_KEY="1"` is loaded as `{PARENT: {CHILD: {KEY: "1"}}}`.
package env

import (
Expand All @@ -22,7 +20,7 @@ import (

// Env is a Provider that loads configuration from environment variables.
//
// To create a new Env, call [New].
// To create a new Env, call New.
type Env struct {
_ [0]func() // Ensure it's incomparable.
prefix string
Expand Down
15 changes: 8 additions & 7 deletions provider/env/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@

package env

// WithPrefix enables loads environment variables with the given prefix in the name.
// WithPrefix provides the prefix used when loading environment variables.
// Only environment variables with names that start with the prefix will be loaded.
//
// E.g. if the given prefix is "server", it only loads environment variables
// which name starts with "server".
// For example, if the prefix is "server", only environment variables whose names start with "server" will be loaded.
// By default, it has no prefix which loads all environment variables.
func WithPrefix(prefix string) Option {
return func(options *options) {
options.prefix = prefix
}
}

// WithDelimiter provides the delimiter when splitting environment variable name to nested keys.
// WithDelimiter provides the delimiter used when splitting environment variable names into nested keys.
//
// The default delimiter is `_`, which loads the environment variable `PARENT_CHILD_KEY="1"`
// as `{PARENT: {CHILD: {KEY: "1"}}}`.
// For example, with the default delimiter "_", an environment variable name like "PARENT_CHILD_KEY"
// would be split into "PARENT", "CHILD", and "KEY".
func WithDelimiter(delimiter string) Option {
return func(options *options) {
options.delimiter = delimiter
}
}

// Option configures the given Env.
// Option configures an Env with specific options.
type Option func(*options)

type options Env
17 changes: 9 additions & 8 deletions provider/file/file.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Copyright (c) 2023 The konf authors
// Use of this source code is governed by a MIT license found in the LICENSE file.

// Package file loads configuration from files.
// Package file loads configuration from OS file.
//
// File loads file with given path from OS file system
// and returns nested map[string]any that is parsed as json.
// File loads a file with the given path from the OS file system and returns
// a nested map[string]any that is parsed with the given unmarshal function.
//
// The default behavior can be changed with following options:
// - WithUnmarshal provides the function that parses config file.
// E.g. `WithUnmarshal(yaml.Unmarshal)` will parse the file as yaml.
// - IgnoreFileNotExit ignores the error if config file does not exist.
// The unmarshal function must be able to unmarshal the file content into a map[string]any.
// For example, with the default json.Unmarshal, the file is parsed as JSON.
//
// By default, it returns error while loading if the file is not found.
// IgnoreFileNotExit can override the behavior to return an empty map[string]any.
package file

import (
Expand All @@ -19,7 +20,7 @@ import (
"os"
)

// File is a Provider that loads configuration from file.
// File is a Provider that loads configuration from a OS file.
//
// To create a new File, call [New].
type File struct {
Expand Down
61 changes: 0 additions & 61 deletions provider/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,9 @@
package file_test

import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"

"github.com/ktong/konf/provider/file"
"github.com/ktong/konf/provider/file/internal/assert"
Expand Down Expand Up @@ -76,62 +71,6 @@ func TestFile_Load(t *testing.T) {
}
}

func TestFile_Watch(t *testing.T) {
testcases := []struct {
description string
action func(string) error
expacted map[string]any
}{
{
description: "create",
action: func(path string) error {
return os.WriteFile(path, []byte(`{"p": {"k": "v"}}`), 0o600)
},
expacted: map[string]any{"p": map[string]any{"k": "v"}},
},
{
description: "write",
action: func(path string) error {
return os.WriteFile(path, []byte(`{"p": {"k": "c"}}`), 0o600)
},
expacted: map[string]any{"p": map[string]any{"k": "c"}},
},
{
description: "remove",
action: os.Remove,
},
}

for i := range testcases {
testcase := testcases[i]

t.Run(testcase.description, func(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "watch.json")
assert.NoError(t, os.WriteFile(tmpFile, []byte(`{"p": {"k": "v"}}`), 0o600))

loader := file.New(tmpFile)
var values map[string]any
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

var waitGroup sync.WaitGroup
waitGroup.Add(1)
go func() {
err := loader.Watch(ctx, func(changed map[string]any) {
defer waitGroup.Done()
values = changed
})
assert.NoError(t, err)
}()

time.Sleep(time.Second)
assert.NoError(t, testcase.action(tmpFile))
waitGroup.Wait()
assert.Equal(t, testcase.expacted, values)
})
}
}

func TestFile_String(t *testing.T) {
t.Parallel()

Expand Down
7 changes: 4 additions & 3 deletions provider/file/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

package file

// WithUnmarshal provides the function that parses config file.
// WithUnmarshal provides the function used to parses the configuration file.
// The unmarshal function must be able to unmarshal the file content into a map[string]any.
//
// The default function is json.Unmarshal.
func WithUnmarshal(unmarshal func([]byte, any) error) Option {
Expand All @@ -12,14 +13,14 @@ func WithUnmarshal(unmarshal func([]byte, any) error) Option {
}
}

// IgnoreFileNotExit ignores the error if config file does not exist.
// IgnoreFileNotExit ignores the error and return an empty map instead if the configuration file is not found.
func IgnoreFileNotExit() Option {
return func(options *options) {
options.ignoreNotExist = true
}
}

// Option configures the given File.
// Option configures the a File with specific options.
type Option func(options *options)

type options File
3 changes: 0 additions & 3 deletions provider/file/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import (
"github.com/fsnotify/fsnotify"
)

// Watch watches the file and triggers a callback when it changes.
// It blocks until ctx is done, or the service returns a non-retryable error.
//
//nolint:cyclop,funlen
func (f File) Watch(ctx context.Context, onChange func(map[string]any)) error {
watcher, err := fsnotify.NewWatcher()
Expand Down
Loading

0 comments on commit 2800ed6

Please sign in to comment.