From addd3d94cca88db1ef6fc81d2fe25d99e581b79f Mon Sep 17 00:00:00 2001 From: Rheisen Date: Wed, 22 May 2024 13:20:14 -0400 Subject: [PATCH 1/7] Working proof-of-concept fill struct functionality --- app_config.go | 75 +++++++++++++++++++++++++++++++++++++++++++++ app_config_test.go | 53 ++++++++++++++++++++++++++++++++ config_struct.go | 3 ++ field_set_struct.go | 5 +++ snake_case.go | 16 ++++++++++ 5 files changed, 152 insertions(+) create mode 100644 config_struct.go create mode 100644 field_set_struct.go create mode 100644 snake_case.go diff --git a/app_config.go b/app_config.go index 665d5be..5f27503 100644 --- a/app_config.go +++ b/app_config.go @@ -3,6 +3,7 @@ package bconf import ( "fmt" "os" + "reflect" "sort" "strings" "sync" @@ -424,6 +425,80 @@ func (c *AppConfig) GetDurations(fieldSetKey, fieldKey string) ([]time.Duration, return val, nil } +func (c *AppConfig) FillStruct(configStruct any) error { + defer func() error { + if r := recover(); r != nil { + return fmt.Errorf("problem filling struct: %s", r) + } + + return nil + }() + + if reflect.TypeOf(configStruct).Kind() != reflect.Pointer { + return fmt.Errorf("FillStruct expects a pointer to a struct, found '%s'", reflect.TypeOf(configStruct).Kind()) + } + + configStructValue := reflect.Indirect(reflect.ValueOf(configStruct)) + configStructType := configStructValue.Type() + + if configStructValue.Kind() != reflect.Struct { + return fmt.Errorf("FillStruct expects a pointer to a struct, found pointer to '%s'", configStructValue.Kind()) + } + + configStructField := configStructValue.FieldByName("ConfigStruct") + if !configStructField.IsValid() || configStructField.Type().PkgPath() != "github.com/rheisen/bconf" { + return fmt.Errorf("FillStruct expects a struct with a bconf.ConfigStruct field, none found") + } + + configStructFieldType, _ := configStructType.FieldByName("ConfigStruct") + + baseFieldSet := configStructFieldType.Tag.Get("bconf") + + for i := 0; i < configStructValue.NumField(); i++ { + field := configStructType.Field(i) + + if field.Name == "ConfigStruct" && field.Type.PkgPath() == "github.com/rheisen/bconf" { + continue + } + + fieldTagValue := field.Tag.Get("bconf") + + var fieldKey string + + fieldSetKey := baseFieldSet + + if fieldTagValue == "" { + fieldKey = field.Name + } else { + fieldTagParams := strings.Split(fieldTagValue, ",") + fieldLocation := strings.Split(fieldTagParams[0], ".") + + fieldKey = fieldLocation[0] + + // NOTE: error if fieldLocation format isn't . ? + if len(fieldLocation) > 1 { + fieldSetKey = fieldLocation[0] + fieldKey = fieldLocation[1] + } + } + + appConfigField, err := c.GetField(fieldSetKey, fieldKey) + if err != nil { + return fmt.Errorf("problem getting field '%s.%s': %w", fieldSetKey, fieldKey, err) + } + + val, err := appConfigField.getValue() + if err != nil { + return fmt.Errorf("problem getting field '%s.%s' value: %w", fieldSetKey, fieldKey, err) + } + + configStructValue.Field(i).Set(reflect.ValueOf(val)) + // fmt.Printf("'%s' Field (type: '%s') bconf tag value = '%s'\n", fieldName, field.Type, fieldTagValue) + } + + return nil +} + // -- Private methods -- func (c *AppConfig) addFieldSet(fieldSet *FieldSet, lock bool) []error { diff --git a/app_config_test.go b/app_config_test.go index 6148b75..436faaa 100644 --- a/app_config_test.go +++ b/app_config_test.go @@ -1586,6 +1586,59 @@ func TestAppConfigTimeFieldTypes(t *testing.T) { } } +func TestAppConfigFillStruct(t *testing.T) { + type TestAPIConfig struct { + bconf.ConfigStruct `bconf:"api"` + Host string `bconf:"host"` + Port int `bconf:"port"` + ReadTimeout time.Duration `bconf:"read_timeout"` + DBSwitchTime time.Time `bconf:"db_switch_time"` + DebugMode bool `bconf:"debug_mode"` + } + + type InvalidAPIConfigStruct struct { + Host string `bconf:"host"` + Port int `bconf:"port"` + } + + configStruct := &TestAPIConfig{} + + appConfig := createBaseAppConfig() + + errs := appConfig.AddFieldSet( + bconf.FSB().Key("api").Fields( + bconf.FB().Key("host").Type(bconf.String).Default("localhost").Create(), + bconf.FB().Key("port").Type(bconf.Int).Default(8080).Create(), + bconf.FB().Key("read_timeout").Type(bconf.Duration).Default(5*time.Second).Create(), + bconf.FB().Key("db_switch_time").Type(bconf.Time).Default(time.Now().Add(-100*time.Hour)).Create(), + bconf.FB().Key("debug_mode").Type(bconf.Bool).Default(true).Create(), + ).Create(), + ) + + if len(errs) > 0 { + t.Fatalf("problem adding field set to AppConfig: %v\n", errs) + } + + unfillableStruct := TestAPIConfig{} + if err := appConfig.FillStruct(unfillableStruct); err == nil { + t.Fatalf("expected error passing concrete struct\n") + } + + notAStruct := 20 + if err := appConfig.FillStruct(¬AStruct); err == nil { + t.Fatalf("expected error passing pointer to a non-struct type\n") + } + + invalidConfigStruct := &InvalidAPIConfigStruct{} + if err := appConfig.FillStruct(invalidConfigStruct); err == nil { + t.Fatalf("expected error passing struct missing bconf.ConfigStruct field\n") + } + + if err := appConfig.FillStruct(configStruct); err != nil { + t.Fatalf("problem setting struct values from AppConfig: %s\n", err) + } +} + func createBaseAppConfig() *bconf.AppConfig { appConfig := bconf.NewAppConfig( "app", diff --git a/config_struct.go b/config_struct.go new file mode 100644 index 0000000..c7539ad --- /dev/null +++ b/config_struct.go @@ -0,0 +1,3 @@ +package bconf + +type ConfigStruct struct{} diff --git a/field_set_struct.go b/field_set_struct.go new file mode 100644 index 0000000..9befdc4 --- /dev/null +++ b/field_set_struct.go @@ -0,0 +1,5 @@ +package bconf + +type FieldSetStruct interface { + FieldSet() string +} diff --git a/snake_case.go b/snake_case.go new file mode 100644 index 0000000..4578e52 --- /dev/null +++ b/snake_case.go @@ -0,0 +1,16 @@ +package bconf + +import ( + "regexp" + "strings" +) + +var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") +var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") + +func toSnakeCase(str string) string { + snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") + snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") + + return strings.ToLower(snake) +} From 0499b3d2454a510dadfba4763b4867d0d4b5c5c6 Mon Sep 17 00:00:00 2001 From: Rheisen Date: Mon, 27 May 2024 08:15:28 -0400 Subject: [PATCH 2/7] Fix linting errors, add test coverage --- app_config.go | 24 +++++++++++++----------- app_config_test.go | 28 +++++++++++++++++++++++++--- json_file_loader.go | 14 ++++++-------- json_file_loader_test.go | 8 -------- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/app_config.go b/app_config.go index 5f27503..50bb39b 100644 --- a/app_config.go +++ b/app_config.go @@ -425,13 +425,11 @@ func (c *AppConfig) GetDurations(fieldSetKey, fieldKey string) ([]time.Duration, return val, nil } -func (c *AppConfig) FillStruct(configStruct any) error { - defer func() error { +func (c *AppConfig) FillStruct(configStruct any) (err error) { + defer func() { if r := recover(); r != nil { - return fmt.Errorf("problem filling struct: %s", r) + err = fmt.Errorf("problem filling struct: %s", r) } - - return nil }() if reflect.TypeOf(configStruct).Kind() != reflect.Pointer { @@ -462,14 +460,15 @@ func (c *AppConfig) FillStruct(configStruct any) error { } fieldTagValue := field.Tag.Get("bconf") - - var fieldKey string - + fieldKey := "" fieldSetKey := baseFieldSet - if fieldTagValue == "" { + switch fieldTagValue { + case "": fieldKey = field.Name - } else { + case "-": + continue + default: fieldTagParams := strings.Split(fieldTagValue, ",") fieldLocation := strings.Split(fieldTagParams[0], ".") @@ -482,6 +481,10 @@ func (c *AppConfig) FillStruct(configStruct any) error { } } + if fieldSetKey == "" { + return fmt.Errorf("unidentified field-set for field: %s", fieldKey) + } + appConfigField, err := c.GetField(fieldSetKey, fieldKey) if err != nil { return fmt.Errorf("problem getting field '%s.%s': %w", fieldSetKey, fieldKey, err) @@ -493,7 +496,6 @@ func (c *AppConfig) FillStruct(configStruct any) error { } configStructValue.Field(i).Set(reflect.ValueOf(val)) - // fmt.Printf("'%s' Field (type: '%s') bconf tag value = '%s'\n", fieldName, field.Type, fieldTagValue) } return nil diff --git a/app_config_test.go b/app_config_test.go index 436faaa..8a6b837 100644 --- a/app_config_test.go +++ b/app_config_test.go @@ -1589,10 +1589,10 @@ func TestAppConfigTimeFieldTypes(t *testing.T) { func TestAppConfigFillStruct(t *testing.T) { type TestAPIConfig struct { bconf.ConfigStruct `bconf:"api"` + DBSwitchTime time.Time `bconf:"db_switch_time"` Host string `bconf:"host"` - Port int `bconf:"port"` ReadTimeout time.Duration `bconf:"read_timeout"` - DBSwitchTime time.Time `bconf:"db_switch_time"` + Port int `bconf:"port"` DebugMode bool `bconf:"debug_mode"` } @@ -1605,12 +1605,14 @@ func TestAppConfigFillStruct(t *testing.T) { appConfig := createBaseAppConfig() + dbSwitchTime := time.Now().Add(-100 * time.Hour) + errs := appConfig.AddFieldSet( bconf.FSB().Key("api").Fields( bconf.FB().Key("host").Type(bconf.String).Default("localhost").Create(), bconf.FB().Key("port").Type(bconf.Int).Default(8080).Create(), bconf.FB().Key("read_timeout").Type(bconf.Duration).Default(5*time.Second).Create(), - bconf.FB().Key("db_switch_time").Type(bconf.Time).Default(time.Now().Add(-100*time.Hour)).Create(), + bconf.FB().Key("db_switch_time").Type(bconf.Time).Default(dbSwitchTime).Create(), bconf.FB().Key("debug_mode").Type(bconf.Bool).Default(true).Create(), ).Create(), ) @@ -1637,6 +1639,26 @@ func TestAppConfigFillStruct(t *testing.T) { if err := appConfig.FillStruct(configStruct); err != nil { t.Fatalf("problem setting struct values from AppConfig: %s\n", err) } + + if configStruct.Host != "localhost" { + t.Errorf("unexpected value for configStruct.Host ('%s'), expected: %s\n", configStruct.Host, "localhost") + } + + if configStruct.Port != 8080 { + t.Errorf("unexpected value for configStruct.Port ('%d'), expected: %d\n", configStruct.Port, 8080) + } + + if configStruct.ReadTimeout != 5*time.Second { + t.Errorf("unexpected value for configStruct.Host ('%s'), expected: %s\n", configStruct.ReadTimeout, 5*time.Second) + } + + if configStruct.DBSwitchTime != dbSwitchTime { + t.Errorf("unexpected value for configStruct.Host ('%s'), expected: %s\n", configStruct.DBSwitchTime, dbSwitchTime) + } + + if configStruct.DebugMode != true { + t.Errorf("unexpected value for configStruct.Host ('%v'), expected: %v\n", configStruct.DebugMode, true) + } } func createBaseAppConfig() *bconf.AppConfig { diff --git a/json_file_loader.go b/json_file_loader.go index 4ea3d10..2578454 100644 --- a/json_file_loader.go +++ b/json_file_loader.go @@ -16,6 +16,10 @@ func NewJSONFileLoader() *JSONFileLoader { } func NewJSONFileLoaderWithAttributes(decoder JSONUnmarshal, filePaths ...string) *JSONFileLoader { + if decoder == nil { + decoder = json.Unmarshal + } + return &JSONFileLoader{ Decoder: decoder, FilePaths: filePaths, @@ -131,14 +135,8 @@ func (l *JSONFileLoader) fileMaps() []map[string]any { } fileMap := map[string]any{} - if l.Decoder != nil { - if err := l.Decoder(fileBytes, &fileMap); err != nil { - continue - } - } else { - if err := json.Unmarshal(fileBytes, &fileMap); err != nil { - continue - } + if err := l.Decoder(fileBytes, &fileMap); err != nil { + continue } fileMaps = append(fileMaps, fileMap) diff --git a/json_file_loader_test.go b/json_file_loader_test.go index 414e410..ef3da72 100644 --- a/json_file_loader_test.go +++ b/json_file_loader_test.go @@ -18,14 +18,6 @@ func TestJSONFileLoaderFunctions(t *testing.T) { loader = bconf.NewJSONFileLoaderWithAttributes(json.Unmarshal, "./fixtures/json_config_test_fixture_01.json") - if loader == nil { - t.Fatalf("unexpected nil loader") - } - - if loader.Decoder == nil { - t.Fatalf("unexpected nil decoder") - } - if len(loader.FilePaths) != 1 { t.Fatalf("unexpected file-paths length '%d', expected '1'", len(loader.FilePaths)) } From 3067340c9fd929f57e768dab20677f87f9274ebc Mon Sep 17 00:00:00 2001 From: Rheisen Date: Wed, 29 May 2024 09:02:42 -0400 Subject: [PATCH 3/7] Update README, ignore empty fields when filling struct --- README.md | 41 ++++++++++++++++++++++++++++++++++++----- app_config.go | 4 +++- field.go | 4 +++- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b9b887b..6e42fbf 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ In Progress ### Getting Values from `bconf.AppConfig` +* `FillStruct(configStruct any) error` * `GetField(fieldSetKey, fieldKey string) (*bconf.Field, error)` * `GetString(fieldSetKey, fieldKey string) (string, error)` * `GetStrings(fieldSetKey, fieldKey string) ([]string, error)` @@ -62,6 +63,7 @@ In Progress * Ability to get a safe map of configuration values from the `bconf.AppConfig` `ConfigMap()` function * (the configuration map will obfuscate values from fields with `Sensitive` parameter set to `true`) * Ability to reload field-sets and individual fields via the `bconf.AppConfig` +* Ability to fill configuration structures with values from a `bconf.AppConfig` ### Limitations @@ -139,10 +141,24 @@ if errs := configuration.Register(true); len(errs) > 0 { // (based on the loaders set above). logLevel, err := configuration.GetString("log", "level") if err != nil { - // handle retrieval error + // handle error } -fmt.Printf("log-level: %s", logLevel) +fmt.Printf("log-level: %s\n", logLevel) + +type loggerConfig struct { + bconf.ConfigStruct `bconf:"log"` + Level string `bconf:"level"` + Format string `bconf:"format"` + ColorEnabled bool `bconf:"color_enabled"` +} + +logConfig := &loggerConfig{} +if err := configuration.FillStruct(logConfig); err != nil { + // handle error +} + +fmt.Printf("log config: %v\n", logConfig) ``` ```go @@ -227,17 +243,32 @@ if errs := configuration.Register(true); len(errs) > 0 { // (based on the loaders set above). logLevel, err := configuration.GetString("log", "level") if err != nil { - // handle retrieval error + // handle error +} + +fmt.Printf("log-level: %s\n", logLevel) + +type loggerConfig struct { + bconf.ConfigStruct `bconf:"log"` + Level string `bconf:"level"` + Format string `bconf:"format"` + ColorEnabled bool `bconf:"color_enabled"` +} + +logConfig := &loggerConfig{} +if err := configuration.FillStruct(logConfig); err != nil { + // handle error } -fmt.Printf("log-level: %s", logLevel) +fmt.Printf("log config: %v\n", logConfig) ``` In both of the code blocks above, a `bconf.AppConfig` is defined with two field-sets (which group configuration related to the application and logging in this case), and registered with help flag parsing. If this code was executed in a `main()` function, it would print the log level picked up by the configuration from the -flags or run-time environment before falling back on the defined default value of "info". +flags or run-time environment before falling back on the defined default value of "info". It would then fill the +`logConfig` struct with multiple values from the log field-set fields, and print those values as well. If this code was executed inside the `main()` function and passed a `--help` or `-h` flag, it would print the following output: diff --git a/app_config.go b/app_config.go index 50bb39b..e05014a 100644 --- a/app_config.go +++ b/app_config.go @@ -491,7 +491,9 @@ func (c *AppConfig) FillStruct(configStruct any) (err error) { } val, err := appConfigField.getValue() - if err != nil { + if err != nil && err.Error() == emptyFieldError { + continue + } else if err != nil { return fmt.Errorf("problem getting field '%s.%s' value: %w", fieldSetKey, fieldKey, err) } diff --git a/field.go b/field.go index 9311bb1..4270bfd 100644 --- a/field.go +++ b/field.go @@ -10,6 +10,8 @@ import ( "github.com/rheisen/bconf/bconfconst" ) +const emptyFieldError = "empty field value" + // Fields is a slice of Field elements providing context for configuration values type Fields []*Field @@ -279,7 +281,7 @@ func (f *Field) getValue() (any, error) { return f.generatedDefault, nil } - return nil, fmt.Errorf("empty field value") + return nil, fmt.Errorf(emptyFieldError) } // func (f *Field) getValueFrom(loader string) (any, error) { From dfdb0b099206c973579f8467696d16f5f8154082 Mon Sep 17 00:00:00 2001 From: Rheisen Date: Tue, 6 Aug 2024 08:49:55 -0400 Subject: [PATCH 4/7] Add ability to override the default field set when filling a struct --- app_config.go | 4 ++++ app_config_test.go | 28 ++++++++++++++++++++++++++++ config_struct.go | 4 +++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app_config.go b/app_config.go index e05014a..3084714 100644 --- a/app_config.go +++ b/app_config.go @@ -452,6 +452,10 @@ func (c *AppConfig) FillStruct(configStruct any) (err error) { baseFieldSet := configStructFieldType.Tag.Get("bconf") + if overrideValue := configStructField.FieldByName("FieldSet"); overrideValue.String() != "" { + baseFieldSet = overrideValue.String() + } + for i := 0; i < configStructValue.NumField(); i++ { field := configStructType.Field(i) diff --git a/app_config_test.go b/app_config_test.go index 8a6b837..74059bf 100644 --- a/app_config_test.go +++ b/app_config_test.go @@ -1621,6 +1621,20 @@ func TestAppConfigFillStruct(t *testing.T) { t.Fatalf("problem adding field set to AppConfig: %v\n", errs) } + errs = appConfig.AddFieldSet( + bconf.FSB().Key("ext_api").Fields( + bconf.FB().Key("host").Type(bconf.String).Default("0.0.0.0").Create(), + bconf.FB().Key("port").Type(bconf.Int).Default(8085).Create(), + bconf.FB().Key("read_timeout").Type(bconf.Duration).Default(10*time.Second).Create(), + bconf.FB().Key("db_switch_time").Type(bconf.Time).Default(dbSwitchTime).Create(), + bconf.FB().Key("debug_mode").Type(bconf.Bool).Default(true).Create(), + ).Create(), + ) + + if len(errs) > 0 { + t.Fatalf("problem adding field set to AppConfig: %v\n", errs) + } + unfillableStruct := TestAPIConfig{} if err := appConfig.FillStruct(unfillableStruct); err == nil { t.Fatalf("expected error passing concrete struct\n") @@ -1659,6 +1673,20 @@ func TestAppConfigFillStruct(t *testing.T) { if configStruct.DebugMode != true { t.Errorf("unexpected value for configStruct.Host ('%v'), expected: %v\n", configStruct.DebugMode, true) } + + overrideConfigStruct := &TestAPIConfig{ConfigStruct: bconf.ConfigStruct{FieldSet: "ext_api"}} + + if err := appConfig.FillStruct(overrideConfigStruct); err != nil { + t.Fatalf("problem setting override struct values from AppConfig: %s\n", err) + } + + if overrideConfigStruct.Host != "0.0.0.0" { + t.Errorf("unexpected value for overrideConfigStruct.Host ('%s'), expected: %s\n", configStruct.Host, "0.0.0.0") + } + + if overrideConfigStruct.Port != 8085 { + t.Errorf("unexpected value for overrideConfigStruct.Port ('%d'), expected: %d\n", configStruct.Port, 8085) + } } func createBaseAppConfig() *bconf.AppConfig { diff --git a/config_struct.go b/config_struct.go index c7539ad..498983a 100644 --- a/config_struct.go +++ b/config_struct.go @@ -1,3 +1,5 @@ package bconf -type ConfigStruct struct{} +type ConfigStruct struct { + FieldSet string +} From b074d6ecc9369e120a0afbacb04aff1f480eb5e1 Mon Sep 17 00:00:00 2001 From: Rheisen Date: Sun, 8 Sep 2024 10:03:09 -0400 Subject: [PATCH 5/7] Add test coverage, remove unused snake casing function --- app_config_test.go | 5 ++++- snake_case.go | 16 ---------------- 2 files changed, 4 insertions(+), 17 deletions(-) delete mode 100644 snake_case.go diff --git a/app_config_test.go b/app_config_test.go index 74059bf..a8768ec 100644 --- a/app_config_test.go +++ b/app_config_test.go @@ -1592,8 +1592,9 @@ func TestAppConfigFillStruct(t *testing.T) { DBSwitchTime time.Time `bconf:"db_switch_time"` Host string `bconf:"host"` ReadTimeout time.Duration `bconf:"read_timeout"` - Port int `bconf:"port"` + Port int `bconf:"api.port"` DebugMode bool `bconf:"debug_mode"` + LogPrefix string `bconf:"log_prefix"` } type InvalidAPIConfigStruct struct { @@ -1614,6 +1615,7 @@ func TestAppConfigFillStruct(t *testing.T) { bconf.FB().Key("read_timeout").Type(bconf.Duration).Default(5*time.Second).Create(), bconf.FB().Key("db_switch_time").Type(bconf.Time).Default(dbSwitchTime).Create(), bconf.FB().Key("debug_mode").Type(bconf.Bool).Default(true).Create(), + bconf.FB().Key("log_prefix").Type(bconf.String).Create(), ).Create(), ) @@ -1628,6 +1630,7 @@ func TestAppConfigFillStruct(t *testing.T) { bconf.FB().Key("read_timeout").Type(bconf.Duration).Default(10*time.Second).Create(), bconf.FB().Key("db_switch_time").Type(bconf.Time).Default(dbSwitchTime).Create(), bconf.FB().Key("debug_mode").Type(bconf.Bool).Default(true).Create(), + bconf.FB().Key("log_prefix").Type(bconf.String).Create(), ).Create(), ) diff --git a/snake_case.go b/snake_case.go deleted file mode 100644 index 4578e52..0000000 --- a/snake_case.go +++ /dev/null @@ -1,16 +0,0 @@ -package bconf - -import ( - "regexp" - "strings" -) - -var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") -var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") - -func toSnakeCase(str string) string { - snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") - snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") - - return strings.ToLower(snake) -} From 582d2cdbabbde6f4a034815d86693005326e13be Mon Sep 17 00:00:00 2001 From: Rheisen Date: Sun, 8 Sep 2024 10:06:30 -0400 Subject: [PATCH 6/7] Fix test case --- app_config_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app_config_test.go b/app_config_test.go index a8768ec..9638d51 100644 --- a/app_config_test.go +++ b/app_config_test.go @@ -1592,8 +1592,8 @@ func TestAppConfigFillStruct(t *testing.T) { DBSwitchTime time.Time `bconf:"db_switch_time"` Host string `bconf:"host"` ReadTimeout time.Duration `bconf:"read_timeout"` - Port int `bconf:"api.port"` - DebugMode bool `bconf:"debug_mode"` + Port int `bconf:"port"` + DebugMode bool `bconf:"api.debug_mode"` LogPrefix string `bconf:"log_prefix"` } From 56d1034459f942353af6056c4b551636373db52a Mon Sep 17 00:00:00 2001 From: Rheisen Date: Sun, 8 Sep 2024 10:08:29 -0400 Subject: [PATCH 7/7] Fix linting error in test case --- app_config_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/app_config_test.go b/app_config_test.go index 9638d51..92606b4 100644 --- a/app_config_test.go +++ b/app_config_test.go @@ -1587,6 +1587,7 @@ func TestAppConfigTimeFieldTypes(t *testing.T) { } func TestAppConfigFillStruct(t *testing.T) { + //nolint:govet // doesn't need to be optimal for tests type TestAPIConfig struct { bconf.ConfigStruct `bconf:"api"` DBSwitchTime time.Time `bconf:"db_switch_time"`