From 4e0f9156cfcf67667dd263ad13f0f9110cbe1798 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Thu, 6 Apr 2023 14:54:28 -0300 Subject: [PATCH] feat!: refactor parse and parse with options (#256) Signed-off-by: Carlos A Becker --- README.md | 31 ++++++------ env.go | 136 ++++++++++++++++++++++------------------------------ env_test.go | 99 +++++++++++++++++++------------------- go.mod | 2 +- 4 files changed, 123 insertions(+), 145 deletions(-) diff --git a/README.md b/README.md index 9d982cb..171239a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://img.shields.io/github/actions/workflow/status/caarlos0/env/build.yml?branch=main&style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build) [![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env) -[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v7) +[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v8) A simple and zero-dependencies library to parse environment variables into structs. @@ -11,7 +11,7 @@ A simple and zero-dependencies library to parse environment variables into struc Get the module with: ```sh -go get github.com/caarlos0/env/v7 +go get github.com/caarlos0/env/v8 ``` The usage looks like this: @@ -23,7 +23,7 @@ import ( "fmt" "time" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type config struct { @@ -110,15 +110,16 @@ of the variable. If you have a type that is not supported out of the box by the lib, you are able to use (or define) and pass custom parsers (and their associated `reflect.Type`) -to the `env.ParseWithFuncs()` function. +to the `env.ParseWithOptions()` function. In addition to accepting a struct pointer (same as `Parse()`), this function -also accepts a `map[reflect.Type]env.ParserFunc`. +also accepts a `Options{}`, and you can set your custom parsers in the `FuncMap` +field. If you add a custom parser for, say `Foo`, it will also be used to parse `*Foo` and `[]Foo` types. -Check the examples in the [go doc](http://pkg.go.dev/github.com/caarlos0/env/v7) +Check the examples in the [go doc](http://pkg.go.dev/github.com/caarlos0/env/v8) for more info. ### A note about `TextUnmarshaler` and `time.Time` @@ -196,7 +197,7 @@ package main import ( "fmt" "time" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type config struct { @@ -245,7 +246,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type Config struct { @@ -283,7 +284,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type Config struct { @@ -319,7 +320,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type Config struct { @@ -353,7 +354,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type Config struct { @@ -403,7 +404,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type Config struct { @@ -442,7 +443,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type Config struct { @@ -476,7 +477,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type Config struct { @@ -509,7 +510,7 @@ import ( "fmt" "log" - "github.com/caarlos0/env/v7" + "github.com/caarlos0/env/v8" ) type Config struct { diff --git a/env.go b/env.go index ce84c62..5e6bcd0 100644 --- a/env.go +++ b/env.go @@ -117,72 +117,60 @@ type Options struct { // name by default if the `env` key is missing. UseFieldNameByDefault bool - // Sets to true if we have already configured once. - configured bool + // Custom parse functions for different types. + FuncMap map[reflect.Type]ParserFunc } -// configure will do the basic configurations and defaults. -func configure(opts []Options) []Options { - // If we have already configured the first item - // of options will have been configured set to true. - if len(opts) > 0 && opts[0].configured { - return opts - } - - // Created options with defaults. - opt := Options{ +func defaultOptions() Options { + return Options{ TagName: "env", Environment: toMap(os.Environ()), - configured: true, + FuncMap: defaultTypeParsers(), } - - // Loop over all opts structs and set - // to opt if value is not default/empty. - for _, item := range opts { - if item.Environment != nil { - opt.Environment = item.Environment - } - if item.TagName != "" { - opt.TagName = item.TagName - } - if item.OnSet != nil { - opt.OnSet = item.OnSet - } - if item.Prefix != "" { - opt.Prefix = item.Prefix - } - opt.UseFieldNameByDefault = item.UseFieldNameByDefault - opt.RequiredIfNoDef = item.RequiredIfNoDef - } - - return []Options{opt} } -func getOnSetFn(opts []Options) OnSetFn { - return opts[0].OnSet +func customOptions(opt Options) Options { + defOpts := defaultOptions() + if opt.TagName == "" { + opt.TagName = defOpts.TagName + } + if opt.Environment == nil { + opt.Environment = defOpts.Environment + } + if opt.FuncMap == nil { + opt.FuncMap = map[reflect.Type]ParserFunc{} + } + for k, v := range defOpts.FuncMap { + opt.FuncMap[k] = v + } + return opt } -// getTagName returns the tag name. -func getTagName(opts []Options) string { - return opts[0].TagName +func optionsWithEnvPrefix(field reflect.StructField, opts Options) Options { + return Options{ + Environment: opts.Environment, + TagName: opts.TagName, + RequiredIfNoDef: opts.RequiredIfNoDef, + OnSet: opts.OnSet, + Prefix: opts.Prefix + field.Tag.Get("envPrefix"), + UseFieldNameByDefault: opts.UseFieldNameByDefault, + FuncMap: opts.FuncMap, + } } -// getEnvironment returns the environment map. -func getEnvironment(opts []Options) map[string]string { - return opts[0].Environment +// Parse parses a struct containing `env` tags and loads its values from +// environment variables. +func Parse(v interface{}) error { + return parseInternal(v, defaultOptions()) } // Parse parses a struct containing `env` tags and loads its values from // environment variables. -func Parse(v interface{}, opts ...Options) error { - return ParseWithFuncs(v, map[reflect.Type]ParserFunc{}, opts...) +func ParseWithOptions(v interface{}, opts Options) error { + return parseInternal(v, customOptions(opts)) } -// ParseWithFuncs is the same as `Parse` except it also allows the user to pass -// in custom parsers. -func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ...Options) error { - opts = configure(opts) - +func parseInternal(v interface{}, opts Options) error { ptrRef := reflect.ValueOf(v) if ptrRef.Kind() != reflect.Ptr { return newAggregateError(NotStructPtrError{}) @@ -191,15 +179,10 @@ func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ... if ref.Kind() != reflect.Struct { return newAggregateError(NotStructPtrError{}) } - parsers := defaultTypeParsers() - for k, v := range funcMap { - parsers[k] = v - } - - return doParse(ref, parsers, opts) + return doParse(ref, opts) } -func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Options) error { +func doParse(ref reflect.Value, opts Options) error { refType := ref.Type() var agrErr AggregateError @@ -208,7 +191,7 @@ func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Opti refField := ref.Field(i) refTypeField := refType.Field(i) - if err := doParseField(refField, refTypeField, funcMap, opts); err != nil { + if err := doParseField(refField, refTypeField, opts); err != nil { if val, ok := err.(AggregateError); ok { agrErr.Errors = append(agrErr.Errors, val.Errors...) } else { @@ -224,15 +207,15 @@ func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Opti return agrErr } -func doParseField(refField reflect.Value, refTypeField reflect.StructField, funcMap map[reflect.Type]ParserFunc, opts []Options) error { +func doParseField(refField reflect.Value, refTypeField reflect.StructField, opts Options) error { if !refField.CanSet() { return nil } if reflect.Ptr == refField.Kind() && !refField.IsNil() { - return ParseWithFuncs(refField.Interface(), funcMap, optsWithPrefix(refTypeField, opts)...) + return parseInternal(refField.Interface(), optionsWithEnvPrefix(refTypeField, opts)) } if reflect.Struct == refField.Kind() && refField.CanAddr() && refField.Type().Name() == "" { - return ParseWithFuncs(refField.Addr().Interface(), funcMap, optsWithPrefix(refTypeField, opts)...) + return parseInternal(refField.Addr().Interface(), optionsWithEnvPrefix(refTypeField, opts)) } value, err := get(refTypeField, opts) if err != nil { @@ -240,11 +223,11 @@ func doParseField(refField reflect.Value, refTypeField reflect.StructField, func } if value != "" { - return set(refField, refTypeField, value, funcMap) + return set(refField, refTypeField, value, opts.FuncMap) } if reflect.Struct == refField.Kind() { - return doParse(refField, funcMap, optsWithPrefix(refTypeField, opts)) + return doParse(refField, optionsWithEnvPrefix(refTypeField, opts)) } return nil @@ -263,20 +246,19 @@ func toEnvName(input string) string { return string(output) } -func get(field reflect.StructField, opts []Options) (val string, err error) { +func get(field reflect.StructField, opts Options) (val string, err error) { var exists bool var isDefault bool var loadFile bool var unset bool var notEmpty bool - required := opts[0].RequiredIfNoDef - prefix := opts[0].Prefix - ownKey, tags := parseKeyForOption(field.Tag.Get(getTagName(opts))) - if ownKey == "" && opts[0].UseFieldNameByDefault { + required := opts.RequiredIfNoDef + ownKey, tags := parseKeyForOption(field.Tag.Get(opts.TagName)) + if ownKey == "" && opts.UseFieldNameByDefault { ownKey = toEnvName(field.Name) } - key := prefix + ownKey + for _, tag := range tags { switch tag { case "": @@ -293,9 +275,12 @@ func get(field reflect.StructField, opts []Options) (val string, err error) { return "", newNoSupportedTagOptionError(tag) } } + + prefix := opts.Prefix + key := prefix + ownKey expand := strings.EqualFold(field.Tag.Get("envExpand"), "true") defaultValue, defExists := field.Tag.Lookup("envDefault") - val, exists, isDefault = getOr(key, defaultValue, defExists, getEnvironment(opts)) + val, exists, isDefault = getOr(key, defaultValue, defExists, opts.Environment) if expand { val = os.ExpandEnv(val) @@ -321,8 +306,8 @@ func get(field reflect.StructField, opts []Options) (val string, err error) { } } - if onSetFn := getOnSetFn(opts); onSetFn != nil { - onSetFn(key, val, isDefault) + if opts.OnSet != nil { + opts.OnSet(key, val, isDefault) } return val, err } @@ -529,12 +514,3 @@ func parseTextUnmarshalers(field reflect.Value, data []string, sf reflect.Struct return nil } - -func optsWithPrefix(field reflect.StructField, opts []Options) []Options { - subOpts := make([]Options, len(opts)) - copy(subOpts, opts) - if prefix := field.Tag.Get("envPrefix"); prefix != "" { - subOpts[0].Prefix += prefix - } - return subOpts -} diff --git a/env_test.go b/env_test.go index 78ca8bf..f03dfc8 100644 --- a/env_test.go +++ b/env_test.go @@ -456,11 +456,11 @@ func TestParseCustomMapType(t *testing.T) { t.Setenv("SECRET_KEY", "somesecretkey:1") var cfg config - isNoErr(t, ParseWithFuncs(&cfg, map[reflect.Type]ParserFunc{ + isNoErr(t, ParseWithOptions(&cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(custommap{}): func(value string) (interface{}, error) { return custommap(map[string]bool{}), nil }, - })) + }})) } func TestParseMapCustomKeyType(t *testing.T) { @@ -473,11 +473,11 @@ func TestParseMapCustomKeyType(t *testing.T) { t.Setenv("SECRET", "somesecretkey:1") var cfg config - isNoErr(t, ParseWithFuncs(&cfg, map[reflect.Type]ParserFunc{ + isNoErr(t, ParseWithOptions(&cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(CustomKey("")): func(value string) (interface{}, error) { return CustomKey(value), nil }, - })) + }})) } func TestParseMapCustomKeyNoParser(t *testing.T) { @@ -518,11 +518,11 @@ func TestParseMapCustomKeyTypeError(t *testing.T) { t.Setenv("SECRET", "somesecretkey:1") var cfg config - err := ParseWithFuncs(&cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(&cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(CustomKey("")): func(value string) (interface{}, error) { return nil, fmt.Errorf("custom error") }, - }) + }}) isTrue(t, errors.Is(err, ParseError{})) } @@ -536,11 +536,11 @@ func TestParseMapCustomValueTypeError(t *testing.T) { t.Setenv("SECRET", "somesecretkey:1") var cfg config - err := ParseWithFuncs(&cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(&cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(Customval("")): func(value string) (interface{}, error) { return nil, fmt.Errorf("custom error") }, - }) + }}) isTrue(t, errors.Is(err, ParseError{})) } @@ -555,7 +555,7 @@ func TestSetenvAndTagOptsChain(t *testing.T) { } cfg := config{} - isNoErr(t, Parse(&cfg, Options{TagName: "mytag"}, Options{Environment: envs})) + isNoErr(t, ParseWithOptions(&cfg, Options{TagName: "mytag", Environment: envs})) isEqual(t, "VALUE1", cfg.Key1) isEqual(t, 3, cfg.Key2) } @@ -570,7 +570,7 @@ func TestJSONTag(t *testing.T) { t.Setenv("KEY2", "5") cfg := config{} - isNoErr(t, Parse(&cfg, Options{TagName: "json"})) + isNoErr(t, ParseWithOptions(&cfg, Options{TagName: "json"})) isEqual(t, "VALUE7", cfg.Key1) isEqual(t, 5, cfg.Key2) } @@ -849,7 +849,7 @@ func TestHook(t *testing.T) { var onSetCalled []onSetArgs - isNoErr(t, Parse(cfg, Options{ + isNoErr(t, ParseWithOptions(cfg, Options{ OnSet: func(tag string, value interface{}, isDefault bool) { onSetCalled = append(onSetCalled, onSetArgs{tag, value, isDefault}) }, @@ -998,11 +998,11 @@ func TestCustomParser(t *testing.T) { cfg := &config{ Other: &bar{}, } - err := ParseWithFuncs(cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(foo{}): func(v string) (interface{}, error) { return foo{name: v}, nil }, - }) + }}) isNoErr(t, err) isEqual(t, cfg.Var.name, "test") @@ -1030,30 +1030,30 @@ func TestIssue226(t *testing.T) { t.Setenv("LMN", "b") cfg := &config{} - isNoErr(t, ParseWithFuncs(cfg, map[reflect.Type]ParserFunc{ + isNoErr(t, ParseWithOptions(cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf([]byte{0}): func(v string) (interface{}, error) { if v == "a" { return []byte("nope"), nil } return []byte(v), nil }, - })) + }})) isEqual(t, cfg.Inner.Abc, []byte("asdasd")) isEqual(t, cfg.Inner.Def, []byte("nope")) isEqual(t, cfg.Hij, []byte("nope")) isEqual(t, cfg.Lmn, []byte("b")) } -func TestParseWithFuncsNoPtr(t *testing.T) { +func TestParseWithOptionsNoPtr(t *testing.T) { type foo struct{} - err := ParseWithFuncs(foo{}, nil) + err := ParseWithOptions(foo{}, Options{}) isErrorWithMessage(t, err, "env: expected a pointer to a Struct") isTrue(t, errors.Is(err, NotStructPtrError{})) } -func TestParseWithFuncsInvalidType(t *testing.T) { +func TestParseWithOptionsInvalidType(t *testing.T) { var c int - err := ParseWithFuncs(&c, nil) + err := ParseWithOptions(&c, Options{}) isErrorWithMessage(t, err, "env: expected a pointer to a Struct") isTrue(t, errors.Is(err, NotStructPtrError{})) } @@ -1074,9 +1074,9 @@ func TestCustomParserError(t *testing.T) { t.Setenv("VAR", "single") cfg := &config{} - err := ParseWithFuncs(cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(foo{}): customParserFunc, - }) + }}) isEqual(t, cfg.Var.name, "") isErrorWithMessage(t, err, `env: parse error on field "Var" of type "env.foo": something broke`) @@ -1090,9 +1090,9 @@ func TestCustomParserError(t *testing.T) { t.Setenv("VAR2", "slice,slace") cfg := &config{} - err := ParseWithFuncs(cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(foo{}): customParserFunc, - }) + }}) isEqual(t, cfg.Var, nil) isErrorWithMessage(t, err, `env: parse error on field "Var" of type "[]env.foo": something broke`) @@ -1120,9 +1120,9 @@ func TestCustomParserBasicType(t *testing.T) { } cfg := &config{} - err := ParseWithFuncs(cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(ConstT(0)): customParserFunc, - }) + }}) isNoErr(t, err) isEqual(t, exp, cfg.Const) @@ -1151,9 +1151,9 @@ func TestCustomParserUint64Alias(t *testing.T) { cfg := config{} - err := ParseWithFuncs(&cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(&cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(one): tParser, - }) + }}) isTrue(t, parserCalled) isNoErr(t, err) @@ -1174,9 +1174,9 @@ func TestTypeCustomParserBasicInvalid(t *testing.T) { } cfg := &config{} - err := ParseWithFuncs(cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(ConstT(0)): customParserFunc, - }) + }}) isEqual(t, cfg.Const, ConstT(0)) isErrorWithMessage(t, err, `env: parse error on field "Const" of type "env.ConstT": random error`) @@ -1201,9 +1201,9 @@ func TestCustomParserNotCalledForNonAlias(t *testing.T) { cfg := config{} - err := ParseWithFuncs(&cfg, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(&cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(T(0)): tParser, - }) + }}) isFalse(t, tParserCalled) isNoErr(t, err) @@ -1328,7 +1328,7 @@ func ExampleParse_onSet() { } os.Setenv("HOME", "/tmp/fakehome") var cfg config - if err := Parse(&cfg, Options{ + if err := ParseWithOptions(&cfg, Options{ OnSet: func(tag string, value interface{}, isDefault bool) { fmt.Printf("Set %s to %v (default? %v)\n", tag, value, isDefault) }, @@ -1410,7 +1410,7 @@ func TestPrecedenceUnmarshalText(t *testing.T) { isEqual(t, []LogLevel{DebugLevel, InfoLevel}, cfg.LogLevels) } -func ExampleParseWithFuncs() { +func ExampleParseWithOptions() { type thing struct { desc string } @@ -1423,11 +1423,11 @@ func ExampleParseWithFuncs() { c := conf{} - err := ParseWithFuncs(&c, map[reflect.Type]ParserFunc{ + err := ParseWithOptions(&c, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(thing{}): func(v string) (interface{}, error) { return thing{desc: v}, nil }, - }) + }}) if err != nil { fmt.Println(err) } @@ -1515,11 +1515,11 @@ func TestCustomSliceType(t *testing.T) { t.Setenv("SECRET_KEY", "somesecretkey") var cfg config - isNoErr(t, ParseWithFuncs(&cfg, map[reflect.Type]ParserFunc{ + isNoErr(t, ParseWithOptions(&cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ reflect.TypeOf(customslice{}): func(value string) (interface{}, error) { return customslice(value), nil }, - })) + }})) } type MyTime time.Time @@ -1556,11 +1556,11 @@ func TestRequiredIfNoDefOption(t *testing.T) { var cfg config t.Run("missing", func(t *testing.T) { - err := Parse(&cfg, Options{RequiredIfNoDef: true}) + err := ParseWithOptions(&cfg, Options{RequiredIfNoDef: true}) isErrorWithMessage(t, err, `env: required environment variable "NAME" is not set; required environment variable "FRUIT" is not set`) isTrue(t, errors.Is(err, EnvVarIsNotSetError{})) t.Setenv("NAME", "John") - err = Parse(&cfg, Options{RequiredIfNoDef: true}) + err = ParseWithOptions(&cfg, Options{RequiredIfNoDef: true}) isErrorWithMessage(t, err, `env: required environment variable "FRUIT" is not set`) isTrue(t, errors.Is(err, EnvVarIsNotSetError{})) }) @@ -1570,7 +1570,7 @@ func TestRequiredIfNoDefOption(t *testing.T) { t.Setenv("FRUIT", "Apple") // should not trigger an error for the missing 'GENRE' env because it has a default value. - isNoErr(t, Parse(&cfg, Options{RequiredIfNoDef: true})) + isNoErr(t, ParseWithOptions(&cfg, Options{RequiredIfNoDef: true})) }) } @@ -1586,23 +1586,24 @@ func TestRequiredIfNoDefNested(t *testing.T) { type config struct { API API `envPrefix:"SERVER_"` } - var cfg config t.Run("missing", func(t *testing.T) { + var cfg config t.Setenv("SERVER_HOST", "https://google.com") t.Setenv("SERVER_TOKEN", "0xdeadfood") - err := Parse(&cfg, Options{RequiredIfNoDef: true}) + err := ParseWithOptions(&cfg, Options{RequiredIfNoDef: true}) isErrorWithMessage(t, err, `env: required environment variable "SERVER_PORT" is not set`) isTrue(t, errors.Is(err, EnvVarIsNotSetError{})) }) t.Run("all set", func(t *testing.T) { + var cfg config t.Setenv("SERVER_HOST", "https://google.com") t.Setenv("SERVER_PORT", "443") t.Setenv("SERVER_TOKEN", "0xdeadfood") - isNoErr(t, Parse(&cfg, Options{RequiredIfNoDef: true})) + isNoErr(t, ParseWithOptions(&cfg, Options{RequiredIfNoDef: true})) }) } @@ -1616,7 +1617,7 @@ func TestPrefix(t *testing.T) { Clean Config } cfg := ComplexConfig{} - isNoErr(t, Parse(&cfg, Options{Environment: map[string]string{"FOO_HOME": "/foo", "BAR_HOME": "/bar", "HOME": "/clean"}})) + isNoErr(t, ParseWithOptions(&cfg, Options{Environment: map[string]string{"FOO_HOME": "/foo", "BAR_HOME": "/bar", "HOME": "/clean"}})) isEqual(t, "/foo", cfg.Foo.Home) isEqual(t, "/bar", cfg.Bar.Home) isEqual(t, "/clean", cfg.Clean.Home) @@ -1637,7 +1638,7 @@ func TestPrefixPointers(t *testing.T) { Bar: &Test{}, Clean: &Test{}, } - isNoErr(t, Parse(&cfg, Options{Environment: map[string]string{"FOO_TEST": "kek", "BAR_TEST": "lel", "TEST": "clean"}})) + isNoErr(t, ParseWithOptions(&cfg, Options{Environment: map[string]string{"FOO_TEST": "kek", "BAR_TEST": "lel", "TEST": "clean"}})) isEqual(t, "kek", cfg.Foo.Str) isEqual(t, "lel", cfg.Bar.Str) isEqual(t, "clean", cfg.Clean.Str) @@ -1650,7 +1651,7 @@ func TestNestedPrefixPointer(t *testing.T) { } `envPrefix:"FOO_"` } cfg := ComplexConfig{} - isNoErr(t, Parse(&cfg, Options{Environment: map[string]string{"FOO_STR": "foo_str"}})) + isNoErr(t, ParseWithOptions(&cfg, Options{Environment: map[string]string{"FOO_STR": "foo_str"}})) isEqual(t, "foo_str", cfg.Foo.Str) type ComplexConfig2 struct { @@ -1662,7 +1663,7 @@ func TestNestedPrefixPointer(t *testing.T) { } `envPrefix:"FOO_"` } cfg2 := ComplexConfig2{} - isNoErr(t, Parse(&cfg2, Options{Environment: map[string]string{"FOO_BAR_STR": "kek", "FOO_BAR2": "lel"}})) + isNoErr(t, ParseWithOptions(&cfg2, Options{Environment: map[string]string{"FOO_BAR_STR": "kek", "FOO_BAR2": "lel"}})) isEqual(t, "lel", cfg2.Foo.Bar2) isEqual(t, "kek", cfg2.Foo.Bar.Str) } @@ -1678,7 +1679,7 @@ func TestComplePrefix(t *testing.T) { Blah string `env:"BLAH"` } cfg := ComplexConfig{} - isNoErr(t, Parse(&cfg, Options{ + isNoErr(t, ParseWithOptions(&cfg, Options{ Prefix: "T_", Environment: map[string]string{ "T_FOO_HOME": "/foo", @@ -1700,7 +1701,7 @@ func TestNoEnvKey(t *testing.T) { bar string } var cfg Config - isNoErr(t, Parse(&cfg, Options{ + isNoErr(t, ParseWithOptions(&cfg, Options{ UseFieldNameByDefault: true, Environment: map[string]string{ "FOO": "fooval", diff --git a/go.mod b/go.mod index b3286f7..273a1aa 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/caarlos0/env/v7 +module github.com/caarlos0/env/v8 go 1.17