From be9656dd263804b020b2690ceaa2a66567560ab8 Mon Sep 17 00:00:00 2001 From: Roman Sharkov Date: Mon, 13 May 2024 01:12:56 +0200 Subject: [PATCH] fix: Improve consistency of error messages Begin error messages with `at :` --- yamagiconf.go | 22 +++++++-------- yamagiconf_test.go | 68 ++++++++++++++++++++++++++-------------------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/yamagiconf.go b/yamagiconf.go index 3e3bfff..2b43bdc 100644 --- a/yamagiconf.go +++ b/yamagiconf.go @@ -534,8 +534,8 @@ func validateYAMLValues(yamlTag, path string, tp reflect.Type, node *yaml.Node) path := path + "." + f.Name contentNode := findContentNodeByTag(node, yamlTag) if contentNode == nil { - return fmt.Errorf("config %q (%s): %w", - yamlTag, path, ErrMissingConfig) + return fmt.Errorf("at %s (as %q): %w", + path, yamlTag, ErrMissingConfig) } err := validateYAMLValues(yamlTag, path, f.Type, contentNode) if err != nil { @@ -616,13 +616,13 @@ func ValidateType[T any]() error { yamlTag := getYAMLFieldName(f.Tag) isExported := f.IsExported() if yamlTag == "" && isExported { - return fmt.Errorf("%s: %w", path+"."+f.Name, ErrMissingYAMLTag) + return fmt.Errorf("at %s: %w", path+"."+f.Name, ErrMissingYAMLTag) } else if yamlTag != "" && !isExported { - return fmt.Errorf("%s: %w", path+"."+f.Name, ErrYAMLTagOnUnexported) + return fmt.Errorf("at %s: %w", path+"."+f.Name, ErrYAMLTagOnUnexported) } if err := validateEnvField(f); err != nil { - return fmt.Errorf("%s: %w", path+"."+f.Name, err) + return fmt.Errorf("at %s: %w", path+"."+f.Name, err) } if !isExported { @@ -639,7 +639,7 @@ func ValidateType[T any]() error { if f.Type.Kind() == reflect.Pointer { switch f.Type.Elem().Kind() { case reflect.Pointer, reflect.Slice, reflect.Map: - return fmt.Errorf("%s: %w", path+"."+f.Name, ErrUnsupportedPtrType) + return fmt.Errorf("at %s: %w", path+"."+f.Name, ErrUnsupportedPtrType) } } @@ -652,7 +652,7 @@ func ValidateType[T any]() error { for _, p := range stack { if p == tp { // Recursive type - return fmt.Errorf("%s: %w", + return fmt.Errorf("at %s: %w", path+"."+f.Name, ErrRecursiveType) } } @@ -666,11 +666,11 @@ func ValidateType[T any]() error { reflect.Func, reflect.Interface, reflect.UnsafePointer: - return fmt.Errorf("%s: %w: %s", + return fmt.Errorf("at %s: %w: %s", path+"."+f.Name, ErrUnsupportedType, tp.String()) case reflect.Int, reflect.Uint: - return fmt.Errorf("%s: %w: %s, %s", + return fmt.Errorf("at %s: %w: %s, %s", path+"."+f.Name, ErrUnsupportedType, tp.String(), "use integer type with specified width, "+ "such as int32 or int64 instead of int") @@ -680,7 +680,7 @@ func ValidateType[T any]() error { case reflect.Map: tp = tp.Elem() if tp.Kind() == reflect.Struct { - return fmt.Errorf("%s: %w: %s, %s", + return fmt.Errorf("at %s: %w: %s, %s", path+"."+f.Name, ErrUnsupportedType, tp.String(), "use pointer to struct as map value") } @@ -690,7 +690,7 @@ func ValidateType[T any]() error { } } if exportedFields < 1 { - return fmt.Errorf("%s: %w", path, ErrNoExportedFields) + return fmt.Errorf("at %s: %w", path, ErrNoExportedFields) } return nil } diff --git a/yamagiconf_test.go b/yamagiconf_test.go index f65ed55..e845335 100644 --- a/yamagiconf_test.go +++ b/yamagiconf_test.go @@ -105,7 +105,7 @@ func TestLoadErrMissingYAMLTag(t *testing.T) { } _, err := LoadSrc[TestConfig]("has-yaml-tag: 'OK'\nNoYAMLTag: 'NO'\n") require.ErrorIs(t, err, yamagiconf.ErrMissingYAMLTag) - require.Equal(t, "TestConfig.NoYAMLTag: missing yaml struct tag", err.Error()) + require.Equal(t, "at TestConfig.NoYAMLTag: missing yaml struct tag", err.Error()) }) t.Run("level_1", func(t *testing.T) { type Foo struct{ NoYAMLTag string } @@ -115,7 +115,9 @@ func TestLoadErrMissingYAMLTag(t *testing.T) { } _, err := LoadSrc[TestConfig]("has-yaml-tag: 'OK'\nfoo:\n NoYAMLTag: 'NO'\n") require.ErrorIs(t, err, yamagiconf.ErrMissingYAMLTag) - require.Equal(t, "TestConfig.Foo.NoYAMLTag: missing yaml struct tag", err.Error()) + require.Equal(t, + "at TestConfig.Foo.NoYAMLTag: missing yaml struct tag", + err.Error()) }) t.Run("slice_item", func(t *testing.T) { @@ -130,7 +132,7 @@ slice: - NoYAMLTag: NO `) require.ErrorIs(t, err, yamagiconf.ErrMissingYAMLTag) - require.Equal(t, "TestConfig.Slice.NoYAMLTag: "+ + require.Equal(t, "at TestConfig.Slice.NoYAMLTag: "+ "missing yaml struct tag", err.Error()) }) @@ -146,7 +148,7 @@ slice: - NoYAMLTag: NO `) require.ErrorIs(t, err, yamagiconf.ErrMissingYAMLTag) - require.Equal(t, "TestConfig.Array.NoYAMLTag: "+ + require.Equal(t, "at TestConfig.Array.NoYAMLTag: "+ "missing yaml struct tag", err.Error()) }) } @@ -158,7 +160,7 @@ func TestLoadInvalidEnvTag(t *testing.T) { } _, err := LoadSrc[TestConfig]("wrong: ok\n") require.ErrorIs(t, err, yamagiconf.ErrInvalidEnvTag) - require.Equal(t, "TestConfig.Wrong: invalid env struct tag: "+ + require.Equal(t, "at TestConfig.Wrong: invalid env struct tag: "+ "must match the POSIX env var regexp: ^[A-Z_][A-Z0-9_]*$", err.Error()) }) @@ -168,7 +170,7 @@ func TestLoadInvalidEnvTag(t *testing.T) { } _, err := LoadSrc[TestConfig]("wrong: ok\n") require.ErrorIs(t, err, yamagiconf.ErrInvalidEnvTag) - require.Equal(t, "TestConfig.Wrong: invalid env struct tag: "+ + require.Equal(t, "at TestConfig.Wrong: invalid env struct tag: "+ "must match the POSIX env var regexp: ^[A-Z_][A-Z0-9_]*$", err.Error()) }) @@ -178,7 +180,7 @@ func TestLoadInvalidEnvTag(t *testing.T) { } _, err := LoadSrc[TestConfig]("wrong: ok\n") require.ErrorIs(t, err, yamagiconf.ErrInvalidEnvTag) - require.Equal(t, "TestConfig.Wrong: invalid env struct tag: "+ + require.Equal(t, "at TestConfig.Wrong: invalid env struct tag: "+ "must match the POSIX env var regexp: ^[A-Z_][A-Z0-9_]*$", err.Error()) }) @@ -192,7 +194,7 @@ func TestLoadInvalidEnvTag(t *testing.T) { _, err := LoadSrc[TestConfig]("container:\n wrong: ok\n") require.ErrorIs(t, err, yamagiconf.ErrInvalidEnvTag) require.Equal(t, - "TestConfig.Container.Wrong: invalid env struct tag: "+ + "at TestConfig.Container.Wrong: invalid env struct tag: "+ "must match the POSIX env var regexp: ^[A-Z_][A-Z0-9_]*$", err.Error()) }) @@ -206,7 +208,7 @@ func TestLoadInvalidEnvTag(t *testing.T) { _, err := LoadSrc[TestConfig]("container:\n ok: ok\n") require.ErrorIs(t, err, yamagiconf.ErrEnvVarOnUnsupportedType) require.Equal(t, - "TestConfig.Container: "+ + "at TestConfig.Container: "+ "env var on unsupported type: yamagiconf_test.Container", err.Error()) }) @@ -220,7 +222,7 @@ func TestLoadInvalidEnvTag(t *testing.T) { _, err := LoadSrc[TestConfig]("container:\n ok: ok\n") require.ErrorIs(t, err, yamagiconf.ErrEnvVarOnUnsupportedType) require.Equal(t, - "TestConfig.Container: "+ + "at TestConfig.Container: "+ "env var on unsupported type: *yamagiconf_test.Container", err.Error()) }) @@ -231,7 +233,7 @@ func TestLoadInvalidEnvTag(t *testing.T) { _, err := LoadSrc[TestConfig]("wrong:\n - ok\n") require.ErrorIs(t, err, yamagiconf.ErrEnvVarOnUnsupportedType) require.Equal(t, - "TestConfig.Wrong: "+ + "at TestConfig.Wrong: "+ "env var on unsupported type: []string", err.Error()) }) } @@ -281,27 +283,27 @@ recurs: t.Run("through_container_ptr", func(t *testing.T) { _, err := LoadSrc[TestConfigRecurThroughContainerPtr](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrRecursiveType) - require.Equal(t, "TestConfigRecurThroughContainerPtr.Container.Recurs: "+ + require.Equal(t, "at TestConfigRecurThroughContainerPtr.Container.Recurs: "+ "recursive type", err.Error()) }) t.Run("ptr_through_container", func(t *testing.T) { _, err := LoadSrc[TestConfigRecurPtrThroughContainer](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrRecursiveType) - require.Equal(t, "TestConfigRecurPtrThroughContainer.Container.Recurs: "+ + require.Equal(t, "at TestConfigRecurPtrThroughContainer.Container.Recurs: "+ "recursive type", err.Error()) }) t.Run("through_slice", func(t *testing.T) { _, err := LoadSrc[TestConfigRecurThroughSlice](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrRecursiveType) - require.Equal(t, "TestConfigRecurThroughSlice.Recurs: "+ + require.Equal(t, "at TestConfigRecurThroughSlice.Recurs: "+ "recursive type", err.Error()) }) t.Run("ptr_through_slice", func(t *testing.T) { _, err := LoadSrc[TestConfigRecurPtrThroughSlice](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrRecursiveType) - require.Equal(t, "TestConfigRecurPtrThroughSlice.Recurs: "+ + require.Equal(t, "at TestConfigRecurPtrThroughSlice.Recurs: "+ "recursive type", err.Error()) }) } @@ -327,7 +329,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedType) - require.Equal(t, "TestConfig.Int: unsupported type: int, "+ + require.Equal(t, "at TestConfig.Int: unsupported type: int, "+ "use integer type with specified width, "+ "such as int32 or int64 instead of int", err.Error()) }) @@ -339,7 +341,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedPtrType) - require.Equal(t, "TestConfig.PtrPtr: unsupported pointer type", err.Error()) + require.Equal(t, "at TestConfig.PtrPtr: unsupported pointer type", err.Error()) }) t.Run("ptr_slice", func(t *testing.T) { @@ -349,7 +351,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedPtrType) - require.Equal(t, "TestConfig.PtrSlice: unsupported pointer type", err.Error()) + require.Equal(t, "at TestConfig.PtrSlice: unsupported pointer type", err.Error()) }) t.Run("ptr_map", func(t *testing.T) { @@ -359,7 +361,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedPtrType) - require.Equal(t, "TestConfig.PtrMap: unsupported pointer type", err.Error()) + require.Equal(t, "at TestConfig.PtrMap: unsupported pointer type", err.Error()) }) t.Run("channel", func(t *testing.T) { @@ -369,7 +371,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedType) - require.Equal(t, "TestConfig.Chan: unsupported type: chan int", err.Error()) + require.Equal(t, "at TestConfig.Chan: unsupported type: chan int", err.Error()) }) t.Run("func", func(t *testing.T) { @@ -379,7 +381,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedType) - require.Equal(t, "TestConfig.Func: unsupported type: func()", err.Error()) + require.Equal(t, "at TestConfig.Func: unsupported type: func()", err.Error()) }) t.Run("unsafe_pointer", func(t *testing.T) { @@ -389,7 +391,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedType) - require.Equal(t, "TestConfig.UnsafePointer: "+ + require.Equal(t, "at TestConfig.UnsafePointer: "+ "unsupported type: unsafe.Pointer", err.Error()) }) @@ -400,7 +402,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedType) - require.Equal(t, "TestConfig.Interface: "+ + require.Equal(t, "at TestConfig.Interface: "+ "unsupported type: interface { Write() ([]uint8, int) }", err.Error()) }) @@ -411,7 +413,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedType) - require.Equal(t, "TestConfig.Anything: "+ + require.Equal(t, "at TestConfig.Anything: "+ "unsupported type: interface {}", err.Error()) }) @@ -426,7 +428,7 @@ recurs: _, err := LoadSrc[TestConfig](yamlContents) require.ErrorIs(t, err, yamagiconf.ErrUnsupportedType) require.Equal(t, - "TestConfig.Map: unsupported type: yamagiconf_test.Foo, "+ + "at TestConfig.Map: unsupported type: yamagiconf_test.Foo, "+ "use pointer to struct as map value", err.Error()) }) @@ -466,7 +468,9 @@ func TestValidateTypeErrYAMLTagOnUnexported(t *testing.T) { } err := yamagiconf.ValidateType[TestConfig]() require.ErrorIs(t, err, yamagiconf.ErrYAMLTagOnUnexported) - require.Equal(t, "TestConfig.unexported: yaml tag on unexported field", err.Error()) + require.Equal(t, + "at TestConfig.unexported: yaml tag on unexported field", + err.Error()) } func TestValidateTypeErrYAmlTagRedefined(t *testing.T) { @@ -489,7 +493,9 @@ func TestValidateTypeErrEnvTagOnUnexported(t *testing.T) { } err := yamagiconf.ValidateType[TestConfig]() require.ErrorIs(t, err, yamagiconf.ErrEnvTagOnUnexported) - require.Equal(t, "TestConfig.unexported: env tag on unexported field", err.Error()) + require.Equal(t, + "at TestConfig.unexported: env tag on unexported field", + err.Error()) } func TestValidateTypeErrNoExportedFields(t *testing.T) { @@ -501,7 +507,7 @@ func TestValidateTypeErrNoExportedFields(t *testing.T) { } err := yamagiconf.ValidateType[TestConfig]() require.ErrorIs(t, err, yamagiconf.ErrNoExportedFields) - require.Equal(t, "TestConfig: no exported fields", err.Error()) + require.Equal(t, "at TestConfig: no exported fields", err.Error()) } func TestValidateTypeErrTagOnInterfaceImpl(t *testing.T) { @@ -583,7 +589,9 @@ func TestAnonymousStructErrorPath(t *testing.T) { } err := yamagiconf.Load(`ok: ok`, &c) require.ErrorIs(t, err, yamagiconf.ErrMissingYAMLTag) - require.Equal(t, "struct{...}.MissingYAMLTag: missing yaml struct tag", err.Error()) + require.Equal(t, + "at struct{...}.MissingYAMLTag: missing yaml struct tag", + err.Error()) } func TestLoadErrMissingConfig(t *testing.T) { @@ -594,7 +602,7 @@ func TestLoadErrMissingConfig(t *testing.T) { _, err := LoadSrc[TestConfig]("ok: 'OK'") require.ErrorIs(t, err, yamagiconf.ErrMissingConfig) require.Equal(t, - `config "missing" (TestConfig.Missing): missing field in config file`, + `at TestConfig.Missing (as "missing"): missing field in config file`, err.Error()) }