diff --git a/.golangci.yml b/.golangci.yml index 391e0d8..b91efc3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,13 @@ -run: - timeout: 5m - -linters-settings: +version: "2" +linters: + default: all + disable: + - noinlineerr + - depguard + - ireturn + # deprecated + - wsl + settings: dupl: threshold: 100 funlen: @@ -12,16 +18,10 @@ linters-settings: min-occurrences: 2 gocyclo: min-complexity: 15 - golint: - min-confidence: 0 - govet: - check-shadowing: true lll: line-length: 140 - maligned: - suggest-new: true misspell: - locale: US + locale: US varnamelen: min-name-length: 2 ignore-decls: @@ -31,43 +31,39 @@ linters-settings: - i int - b bytes.Buffer - h Handle - -linters: - enable-all: true - disable: - - exhaustivestruct - - maligned - - interfacer - - scopelint - - exhaustruct - - depguard - - nolintlint - #deprecated - - structcheck - - varcheck - - golint - - deadcode - - ifshort - - nosnakecase - - - ireturn # implement provider interface - -issues: - # Excluding configuration per-path, per-linter, per-text and per-source - exclude-rules: - - path: _test\.go - linters: - - gomnd - - exhaustivestruct - - wrapcheck - - exhaustruct - - varnamelen - - tenv - - funlen - - path: test/* - linters: - - gomnd - - exhaustivestruct - - wrapcheck - - exhaustruct - - varnamelen + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - exhaustivestruct + - exhaustruct + - funlen + - mnd + - tenv + - varnamelen + - wrapcheck + path: _test\.go + - linters: + - exhaustivestruct + - exhaustruct + - mnd + - varnamelen + - wrapcheck + path: test/* + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + default: all + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/client.go b/client.go index d5928f7..5fd5366 100644 --- a/client.go +++ b/client.go @@ -8,7 +8,7 @@ import ( "sync/atomic" ) -func Must(providers ...interface{}) *Client { +func Must(providers ...any) *Client { client, err := New(providers...) if err != nil { panic(err) @@ -17,7 +17,7 @@ func Must(providers ...interface{}) *Client { return client } -func New(providers ...interface{}) (*Client, error) { +func New(providers ...any) (*Client, error) { client := &Client{ providers: make([]Provider, len(providers)), } @@ -31,6 +31,9 @@ func New(providers ...interface{}) (*Client, error) { factory: func(ctx context.Context) (Provider, error) { return current(ctx, client) }, + mu: sync.Mutex{}, + done: 0, + provider: nil, } default: return nil, fmt.Errorf("provier[%d]: %w %T", idx, ErrUnknowType, prov) @@ -47,23 +50,6 @@ type provider struct { factory func(ctx context.Context) (Provider, error) } -func (p *provider) init(ctx context.Context) error { - if atomic.LoadUint32(&p.done) == 0 { - if !p.mu.TryLock() { - return fmt.Errorf("%w", ErrInitFactory) - } - defer atomic.StoreUint32(&p.done, 1) - defer p.mu.Unlock() - - var err error - if p.provider, err = p.factory(ctx); err != nil { - return fmt.Errorf("init provider factory:%w", err) - } - } - - return nil -} - func (p *provider) Watch(ctx context.Context, callback WatchCallback, path ...string) error { if err := p.init(ctx); err != nil { return fmt.Errorf("init read:%w", err) @@ -94,6 +80,23 @@ func (p *provider) Value(ctx context.Context, path ...string) (Value, error) { return variable, nil } +func (p *provider) init(ctx context.Context) error { + if atomic.LoadUint32(&p.done) == 0 { + if !p.mu.TryLock() { + return fmt.Errorf("%w", ErrInitFactory) + } + defer atomic.StoreUint32(&p.done, 1) + defer p.mu.Unlock() + + var err error + if p.provider, err = p.factory(ctx); err != nil { + return fmt.Errorf("init provider factory:%w", err) + } + } + + return nil +} + type Client struct { providers []Provider } @@ -111,7 +114,7 @@ func (c *Client) Value(ctx context.Context, path ...string) (Value, error) { for _, provider := range c.providers { value, err = provider.Value(ctx, path...) - if err == nil || !(errors.Is(err, ErrValueNotFound) || errors.Is(err, ErrInitFactory)) { + if err == nil || (!errors.Is(err, ErrValueNotFound) && !errors.Is(err, ErrInitFactory)) { break } } diff --git a/client_example_test.go b/client_example_test.go index e4a8845..fe802be 100644 --- a/client_example_test.go +++ b/client_example_test.go @@ -79,7 +79,7 @@ func ExampleClient_Watch() { wg := sync.WaitGroup{} wg.Add(1) - err = watcher.Watch(ctx, func(ctx context.Context, oldVar, newVar config.Value) error { + err = watcher.Watch(ctx, func(_ context.Context, oldVar, newVar config.Value) error { fmt.Println("update example_enable old: ", oldVar.Bool(), " new:", newVar.Bool()) wg.Done() @@ -93,7 +93,7 @@ func ExampleClient_Watch() { _ = os.Setenv("FDEVS_CONFIG_EXAMPLE_ENABLE", "false") - err = watcher.Watch(ctx, func(ctx context.Context, oldVar, newVar config.Value) error { + err = watcher.Watch(ctx, func(_ context.Context, oldVar, newVar config.Value) error { fmt.Println("update example_db_dsn old: ", oldVar.String(), " new:", newVar.String()) wg.Done() diff --git a/definition/option/view.go b/definition/option/view.go index 46e8135..09912f7 100644 --- a/definition/option/view.go +++ b/definition/option/view.go @@ -141,7 +141,7 @@ func (v View) Parse(valName string, value string, keys []string) string { return data } -//nolint:gochecknoglobals,unparam +//nolint:gochecknoglobals var parses = map[string]func(data ParseData) (string, error){ typesIntreface[0].Name(): func(data ParseData) (string, error) { var b bytes.Buffer diff --git a/go.mod b/go.mod index 45682a4..57aae79 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module gitoa.ru/go-4devs/config -go 1.21 +go 1.23 diff --git a/provider/arg/provider.go b/provider/arg/provider.go index 6104f19..4160eff 100644 --- a/provider/arg/provider.go +++ b/provider/arg/provider.go @@ -43,8 +43,44 @@ type Provider struct { name string } -// nolint: cyclop -// return name, value, error. +func (p *Provider) Name() string { + return p.name +} + +func (p *Provider) Value(_ context.Context, path ...string) (config.Value, error) { + err := p.parse() + if err != nil { + return nil, err + } + + name := p.key(path...) + if val, ok := p.args[name]; ok { + switch { + case len(val) == 1: + return value.JString(val[0]), nil + default: + data, jerr := json.Marshal(val) + if jerr != nil { + return nil, fmt.Errorf("failed load data:%w", jerr) + } + + return value.Decode(func(v any) error { + err := json.Unmarshal(data, v) + if err != nil { + return fmt.Errorf("unmarshal:%w", err) + } + + return nil + }), nil + } + } + + return nil, fmt.Errorf("%s:%w", p.Name(), config.ErrValueNotFound) +} + +// parseOne return name, value, error. +// +//nolint:cyclop func (p *Provider) parseOne(arg string) (string, string, error) { if arg[0] != '-' { return "", "", nil @@ -101,36 +137,3 @@ func (p *Provider) parse() error { return nil } - -func (p *Provider) Name() string { - return p.name -} - -func (p *Provider) Value(_ context.Context, path ...string) (config.Value, error) { - if err := p.parse(); err != nil { - return nil, err - } - - name := p.key(path...) - if val, ok := p.args[name]; ok { - switch { - case len(val) == 1: - return value.JString(val[0]), nil - default: - data, jerr := json.Marshal(val) - if jerr != nil { - return nil, fmt.Errorf("failed load data:%w", jerr) - } - - return value.Decode(func(v interface{}) error { - if err := json.Unmarshal(data, v); err != nil { - return fmt.Errorf("unmarshal:%w", err) - } - - return nil - }), nil - } - } - - return nil, fmt.Errorf("%s:%w", p.Name(), config.ErrValueNotFound) -} diff --git a/provider/arg/provider_test.go b/provider/arg/provider_test.go index b029e62..179728c 100644 --- a/provider/arg/provider_test.go +++ b/provider/arg/provider_test.go @@ -65,5 +65,5 @@ func (d *Duration) UnmarshalJSON(in []byte) error { } func (d *Duration) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf("%q", d)), nil + return fmt.Appendf(nil, "%q", d), nil } diff --git a/provider/env/provider.go b/provider/env/provider.go index 161583d..1b6115c 100644 --- a/provider/env/provider.go +++ b/provider/env/provider.go @@ -26,6 +26,7 @@ func New(namespace, appName string, opts ...Option) *Provider { return strings.ToUpper(strings.Join(path, "_")) }, prefix: strings.ToUpper(namespace + "_" + appName + "_"), + name: "", } for _, opt := range opts { diff --git a/provider/env/provider_test.go b/provider/env/provider_test.go index 501f86a..3cfa7b1 100644 --- a/provider/env/provider_test.go +++ b/provider/env/provider_test.go @@ -1,7 +1,6 @@ package env_test import ( - "os" "testing" "gitoa.ru/go-4devs/config/provider/env" @@ -9,10 +8,8 @@ import ( ) func TestProvider(t *testing.T) { - t.Parallel() - - os.Setenv("FDEVS_CONFIG_DSN", test.DSN) - os.Setenv("FDEVS_CONFIG_PORT", "8080") + t.Setenv("FDEVS_CONFIG_DSN", test.DSN) + t.Setenv("FDEVS_CONFIG_PORT", "8080") provider := env.New("fdevs", "config") diff --git a/provider/watcher/provider.go b/provider/watcher/provider.go index dc286e0..fa368ea 100644 --- a/provider/watcher/provider.go +++ b/provider/watcher/provider.go @@ -39,18 +39,20 @@ type Option func(*Provider) type Provider struct { config.Provider + duration time.Duration logger func(context.Context, string, ...any) } func (p *Provider) Watch(ctx context.Context, callback config.WatchCallback, key ...string) error { - old, err := p.Provider.Value(ctx, key...) + old, err := p.Value(ctx, key...) if err != nil { return fmt.Errorf("failed watch variable: %w", err) } go func(oldVar config.Value) { ticker := time.NewTicker(p.duration) + defer func() { ticker.Stop() }() @@ -58,7 +60,7 @@ func (p *Provider) Watch(ctx context.Context, callback config.WatchCallback, key for { select { case <-ticker.C: - newVar, err := p.Provider.Value(ctx, key...) + newVar, err := p.Value(ctx, key...) if err != nil { p.logger(ctx, "get value%v:%v", key, err.Error()) } else if !newVar.IsEquals(oldVar) { @@ -66,8 +68,10 @@ func (p *Provider) Watch(ctx context.Context, callback config.WatchCallback, key if errors.Is(err, config.ErrStopWatch) { return } + p.logger(ctx, "callback %v:%v", key, err) } + oldVar = newVar } case <-ctx.Done(): diff --git a/provider/watcher/provider_test.go b/provider/watcher/provider_test.go index 68725b3..f7e4e30 100644 --- a/provider/watcher/provider_test.go +++ b/provider/watcher/provider_test.go @@ -34,6 +34,7 @@ func TestWatcher(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer func() { cancel() }() @@ -48,9 +49,10 @@ func TestWatcher(t *testing.T) { err := w.Watch( ctx, - func(ctx context.Context, oldVar, newVar config.Value) error { + func(_ context.Context, _, _ config.Value) error { atomic.AddInt32(&cnt, 1) wg.Done() + if atomic.LoadInt32(&cnt) == 2 { return config.ErrStopWatch } diff --git a/test/assert/equal.go b/test/assert/equal.go index 0454954..618d426 100644 --- a/test/assert/equal.go +++ b/test/assert/equal.go @@ -5,7 +5,7 @@ import ( "testing" ) -func Equal(t *testing.T, expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { +func Equal(t *testing.T, expected any, actual any, msgAndArgs ...any) bool { t.Helper() if reflect.DeepEqual(expected, actual) { @@ -17,7 +17,7 @@ func Equal(t *testing.T, expected interface{}, actual interface{}, msgAndArgs .. return false } -func Equalf(t *testing.T, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { +func Equalf(t *testing.T, expected any, actual any, msg string, args ...any) bool { t.Helper() if reflect.DeepEqual(expected, actual) { diff --git a/test/assert/nil.go b/test/assert/nil.go index acd0227..1a39329 100644 --- a/test/assert/nil.go +++ b/test/assert/nil.go @@ -2,7 +2,7 @@ package assert import "testing" -func Nil(t *testing.T, data any, msgAndArgs ...interface{}) bool { +func Nil(t *testing.T, data any, msgAndArgs ...any) bool { t.Helper() if data != nil { diff --git a/test/provider_suite.go b/test/provider_suite.go index cbb594d..2000831 100644 --- a/test/provider_suite.go +++ b/test/provider_suite.go @@ -49,7 +49,7 @@ func NewReadConfig(key ...string) Read { return NewReadUnmarshal(ex, &Config{}, key...) } -func NewReadUnmarshal(expected, target interface{}, key ...string) Read { +func NewReadUnmarshal(expected, target any, key ...string) Read { return Read{ Key: key, Assert: func(t *testing.T, v config.Value) { @@ -66,17 +66,21 @@ func Time(value string) time.Time { return t } -// nolint: cyclop -func NewRead(expected interface{}, key ...string) Read { +// NewRead test data. +// +//nolint:cyclop +func NewRead(expected any, key ...string) Read { return Read{ Key: key, Assert: func(t *testing.T, v config.Value) { t.Helper() + var ( - val interface{} + val any err error - short interface{} + short any ) + switch expected.(type) { case bool: val, err = v.ParseBool() diff --git a/test/require/equal.go b/test/require/equal.go index f2d7dca..e6444ef 100644 --- a/test/require/equal.go +++ b/test/require/equal.go @@ -6,7 +6,7 @@ import ( "gitoa.ru/go-4devs/config/test/assert" ) -func Equal(t *testing.T, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { +func Equal(t *testing.T, expected any, actual any, msgAndArgs ...any) { t.Helper() if assert.Equal(t, expected, actual, msgAndArgs...) { @@ -16,7 +16,7 @@ func Equal(t *testing.T, expected interface{}, actual interface{}, msgAndArgs .. t.FailNow() } -func Equalf(t *testing.T, expected interface{}, actual interface{}, msg string, args ...interface{}) { +func Equalf(t *testing.T, expected any, actual any, msg string, args ...any) { t.Helper() if assert.Equalf(t, expected, actual, msg, args...) { diff --git a/test/require/error.go b/test/require/error.go index 3e1585f..0c3d65b 100644 --- a/test/require/error.go +++ b/test/require/error.go @@ -4,7 +4,7 @@ import ( "testing" ) -func NoError(t *testing.T, err error, msgAndArgs ...interface{}) { +func NoError(t *testing.T, err error, msgAndArgs ...any) { t.Helper() if err != nil { @@ -13,7 +13,7 @@ func NoError(t *testing.T, err error, msgAndArgs ...interface{}) { } } -func NoErrorf(t *testing.T, err error, msg string, args ...interface{}) { +func NoErrorf(t *testing.T, err error, msg string, args ...any) { t.Helper() if err != nil { diff --git a/test/require/fail.go b/test/require/fail.go index ab5cbbe..baee1b1 100644 --- a/test/require/fail.go +++ b/test/require/fail.go @@ -2,7 +2,7 @@ package require import "testing" -func Fail(t *testing.T, msg string, args ...interface{}) { +func Fail(t *testing.T, msg string, args ...any) { t.Helper() t.Errorf(msg, args...) t.FailNow() diff --git a/test/require/true.go b/test/require/true.go index 47efcc1..263c352 100644 --- a/test/require/true.go +++ b/test/require/true.go @@ -4,7 +4,7 @@ import ( "testing" ) -func Truef(t *testing.T, value bool, msg string, args ...interface{}) { +func Truef(t *testing.T, value bool, msg string, args ...any) { t.Helper() if !value { diff --git a/value.go b/value.go index 1095b9a..be4a737 100644 --- a/value.go +++ b/value.go @@ -12,7 +12,7 @@ type Value interface { } type UnmarshalValue interface { - Unmarshal(val interface{}) error + Unmarshal(val any) error } type ReadValue interface { diff --git a/value/decode.go b/value/decode.go index eaf8a61..98be909 100644 --- a/value/decode.go +++ b/value/decode.go @@ -1,4 +1,6 @@ -// nolint: nonamedreturns +// Package value decode value. +// +//nolint:nonamedreturns package value import ( @@ -9,9 +11,9 @@ import ( var _ config.Value = (*Decode)(nil) -type Decode func(v interface{}) error +type Decode func(v any) error -func (s Decode) Unmarshal(v interface{}) error { +func (s Decode) Unmarshal(v any) error { return s(v) } diff --git a/value/empty.go b/value/empty.go index 6b889ad..ff11a5d 100644 --- a/value/empty.go +++ b/value/empty.go @@ -8,7 +8,7 @@ type Empty struct { Err error } -func (e Empty) Unmarshal(_ interface{}) error { +func (e Empty) Unmarshal(_ any) error { return e.Err } diff --git a/value/helpers.go b/value/helpers.go index 5e93d09..18d9f55 100644 --- a/value/helpers.go +++ b/value/helpers.go @@ -72,7 +72,7 @@ func ParseBool(s string) (bool, error) { return b, nil } -func JUnmarshal(b []byte, v interface{}) error { +func JUnmarshal(b []byte, v any) error { if err := json.Unmarshal(b, v); err != nil { return fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } diff --git a/value/jbytes.go b/value/jbytes.go index 051d637..9d79ba5 100644 --- a/value/jbytes.go +++ b/value/jbytes.go @@ -10,7 +10,7 @@ var _ config.Value = (*JBytes)(nil) type JBytes []byte -func (s JBytes) Unmarshal(v interface{}) error { +func (s JBytes) Unmarshal(v any) error { return JUnmarshal(s.Bytes(), v) } diff --git a/value/jstring.go b/value/jstring.go index e9d752c..c4b5b79 100644 --- a/value/jstring.go +++ b/value/jstring.go @@ -10,7 +10,7 @@ var _ config.Value = (*JString)(nil) type JString string -func (s JString) Unmarshal(v interface{}) error { +func (s JString) Unmarshal(v any) error { return JUnmarshal(s.Bytes(), v) } diff --git a/value/value.go b/value/value.go index f2d5f79..5517675 100644 --- a/value/value.go +++ b/value/value.go @@ -11,7 +11,7 @@ import ( var _ config.Value = (*Value)(nil) type Value struct { - Val interface{} + Val any } func (s Value) Int() int { @@ -62,7 +62,7 @@ func (s Value) Duration() time.Duration { return v } -func (s Value) Raw() interface{} { +func (s Value) Raw() any { return s.Val } @@ -72,7 +72,7 @@ func (s Value) Time() time.Time { return v } -func (s Value) Unmarshal(target interface{}) error { +func (s Value) Unmarshal(target any) error { if v, ok := s.Raw().([]byte); ok { err := json.Unmarshal(v, target) if err != nil {