Skip to content

Commit

Permalink
split file and fs provider (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
ktong authored Nov 12, 2023
1 parent f0cdfba commit 8ad0e2f
Show file tree
Hide file tree
Showing 30 changed files with 352 additions and 246 deletions.
4 changes: 4 additions & 0 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ component_management:
- component_id: konf
paths:
- "!provider/pflag/"
- "!provider/file/"
- component_id: file
paths:
- "provider/file/"
- component_id: pflag
paths:
- "provider/pflag/"
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ updates:
schedule:
interval: daily

- package-ecosystem: gomod
directory: /provider/file
schedule:
interval: daily

- package-ecosystem: gomod
directory: /provider/pflag
schedule:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
go-version: 'stable'
- name: Benchmark
run: go test -v -shuffle=on -bench=. ./...
- name: Benchmark (file)
run: go test -v -shuffle=on -bench=. ./...
working-directory: provider/file
- name: Benchmark (pflag)
run: go test -v -shuffle=on -bench=. ./...
working-directory: provider/pflag
3 changes: 3 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
go-version: 'stable'
- name: Coverage
run: go test -v -covermode=count -coverprofile=coverage.txt ./...
- name: Coverage (file)
run: go test -v -covermode=count -coverprofile=coverage.txt ./...
working-directory: provider/file
- name: Coverage (pflag)
run: go test -v -covermode=count -coverprofile=coverage.txt ./...
working-directory: provider/pflag
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
go-version: 'stable'
- name: Lint
uses: golangci/golangci-lint-action@v3
- name: Lint (file)
uses: golangci/golangci-lint-action@v3
with:
working-directory: provider/file
- name: Lint (pflag)
uses: golangci/golangci-lint-action@v3
with:
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ jobs:
run: go test -v -shuffle=on -count=10 -race ./...
- name: Test
run: go test -v -shuffle=on ./...
- name: Race Test (file)
run: go test -v -shuffle=on -count=10 -race ./...
working-directory: provider/file
- name: Test (file)
run: go test -v -shuffle=on ./...
working-directory: provider/file
- name: Race Test (pflag)
run: go test -v -shuffle=on -count=10 -race ./...
working-directory: provider/pflag
Expand Down
9 changes: 1 addition & 8 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ linters-settings:
local-prefixes: github.com/ktong/konf
govet:
check-shadowing: true
ireturn:
allow:
- anon
- error
- empty
- stdlib
- T
makezero:
always: true
misspell:
Expand Down Expand Up @@ -86,7 +79,7 @@ linters:
- govet
- grouper
- importas
# inamedparam
- inamedparam
- ineffassign
- interfacebloat
- ireturn
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed

- Split file and fs provider (#49).

### Removed

- Remove konf.Logger in favor of slog (#48).
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ There are providers for the following configuration sources:
- `env` loads configuration from environment variables.
- `file` loads configuration from a file.
- `flag` loads configuration from flags.
- `fs` loads configuration from fs.FS.
- `pflag` loads configuration from [spf13/pflag](https://github.com/spf13/pflag).

## Compatibility
Expand Down
11 changes: 6 additions & 5 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
type Config struct {
delimiter string

values map[string]any
values *provider
providers []*provider
watchOnce sync.Once
}
Expand All @@ -29,6 +29,7 @@ type Config struct {
func New(opts ...Option) (*Config, error) {
option := apply(opts)
config := option.Config
config.values = &provider{values: make(map[string]any)}
config.providers = make([]*provider, 0, len(option.loaders))

for _, loader := range option.loaders {
Expand All @@ -44,7 +45,7 @@ func New(opts ...Option) (*Config, error) {
if err != nil {
return nil, fmt.Errorf("[konf] load configuration: %w", err)
}
maps.Merge(config.values, values)
maps.Merge(config.values.values, values)
slog.Info(
"Configuration has been loaded.",
"loader", loader,
Expand Down Expand Up @@ -92,10 +93,10 @@ func (c *Config) Unmarshal(path string, target any) error {

func (c *Config) sub(path string) any {
if path == "" {
return c.values
return c.values.values
}

var next any = c.values
var next any = c.values.values
for _, key := range strings.Split(strings.ToLower(path), c.delimiter) {
mp, ok := next.(map[string]any)
if !ok {
Expand Down Expand Up @@ -142,7 +143,7 @@ func (c *Config) Watch(ctx context.Context, fns ...func(*Config)) error { //noli
for _, w := range c.providers {
maps.Merge(values, w.values)
}
c.values = values
c.values.values = values

for _, fn := range fns {
fn(c)
Expand Down
21 changes: 0 additions & 21 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"testing"

"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"

"github.com/ktong/konf"
)
Expand Down Expand Up @@ -179,26 +178,6 @@ func TestConfig_Watch(t *testing.T) {
require.Equal(t, "changed", cfg)
}

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

config, err := konf.New(konf.WithLoader(mapWatcher(make(chan map[string]any))))
require.NoError(t, err)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

group, ctx := errgroup.WithContext(ctx)
group.Go(func() error {
return config.Watch(ctx)
})
group.Go(func() error {
return config.Watch(ctx)
})

require.EqualError(t, group.Wait(), "[konf] Watch only can be called once")
}

type mapWatcher chan map[string]any

func (m mapWatcher) Load() (map[string]any, error) {
Expand Down
28 changes: 2 additions & 26 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@
package konf_test

import (
"context"
"embed"
"fmt"
"time"

"golang.org/x/sync/errgroup"

"github.com/ktong/konf"
"github.com/ktong/konf/provider/env"
"github.com/ktong/konf/provider/file"
pfs "github.com/ktong/konf/provider/fs"
)

func ExampleGet() {
Expand Down Expand Up @@ -42,33 +38,13 @@ func ExampleUnmarshal() {
// Output: example.com:8080
}

func ExampleWatch() {
ExampleSetGlobal()

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

group, ctx := errgroup.WithContext(ctx)
group.Go(func() error {
return konf.Watch(ctx, func() {
fmt.Print(konf.Get[string]("server.host"))
})
})

if err := group.Wait(); err != nil {
// Handle error here.
panic(err)
}
// Output:
}

//go:embed testdata
var testdata embed.FS

func ExampleSetGlobal() {
cfg, err := konf.New(
konf.WithLoader(
file.New("testdata/config.json", file.WithFS(testdata)),
pfs.New(testdata, "testdata/config.json"),
env.New(env.WithPrefix("server")),
),
)
Expand Down
7 changes: 1 addition & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,16 @@ module github.com/ktong/konf

go 1.21

require (
github.com/fsnotify/fsnotify v1.7.0
github.com/mitchellh/mapstructure v1.5.0
)
require github.com/mitchellh/mapstructure v1.5.0

require ( // for test
github.com/stretchr/testify v1.8.4
go.uber.org/goleak v1.3.0
golang.org/x/sync v0.5.0
)

require ( // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
Expand All @@ -14,11 +13,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
1 change: 0 additions & 1 deletion option.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ func apply(opts []Option) options {
option := &options{
Config: &Config{
delimiter: ".",
values: make(map[string]any),
},
}
for _, opt := range opts {
Expand Down
14 changes: 7 additions & 7 deletions provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "context"

// Loader is the interface that wraps the Load method.
//
// Load loads configuration and returns as a nested map[string]any.
// 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.
type Loader interface {
Expand All @@ -16,17 +16,17 @@ type Loader interface {

// Watcher is the interface that wraps the Watch method.
//
// Watch watches configuration and triggers a callback with full new configurations
// as a nested map[string]any when it changes.
// It blocks until ctx is done, or the service returns an error.
// Watch watches configuration and triggers onChange callback with 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 {
Watch(context.Context, func(map[string]any)) error
Watch(ctx context.Context, onChange func(map[string]any)) error
}

// ConfigAware is the interface that wraps the WithConfig method.
//
// WithConfig enables provider loads configuration from providers before it.
// WithConfig enables provider uses configuration loaded by providers before it.
// It ensures the WithConfig is called before executing methods in Loader and Watcher.
type ConfigAware interface {
WithConfig(*Config)
WithConfig(config *Config)
}
17 changes: 2 additions & 15 deletions provider/file/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,16 @@ package file_test

import (
"testing"
"testing/fstest"

"github.com/stretchr/testify/require"

"github.com/ktong/konf/provider/file"
)

func BenchmarkNew(b *testing.B) {
mapFS := fstest.MapFS{
"config.json": {
Data: []byte(`{"k":"v"}`),
},
}
b.ResetTimer()

var loader file.File
for i := 0; i < b.N; i++ {
loader = file.New("config.json", file.WithFS(mapFS))
loader = file.New("testdata/config.json")
}
b.StopTimer()

Expand All @@ -32,12 +24,7 @@ func BenchmarkNew(b *testing.B) {
}

func BenchmarkLoad(b *testing.B) {
fs := fstest.MapFS{
"config.json": {
Data: []byte(`{"k":"v"}`),
},
}
loader := file.New("config.json", file.WithFS(fs))
loader := file.New("testdata/config.json")
b.ResetTimer()

var (
Expand Down
Loading

0 comments on commit 8ad0e2f

Please sign in to comment.