Skip to content

Commit

Permalink
Add strict parsing option (#19)
Browse files Browse the repository at this point in the history
It's useful to fail when extra keys are present in the
config file. Strict parsing is akin to json.DisallowUnknownFields
and yaml.UnmarshalStrict and returns an error upon encountering
an unknown field.
  • Loading branch information
arieltorti authored Mar 25, 2023
1 parent 461f223 commit b6a316a
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 0 deletions.
5 changes: 5 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ Change the layout fig uses to parse times using `TimeLayout()`.
By default fig parses time using the `RFC.3339` layout (`2006-01-02T15:04:05Z07:00`).
# Strict Parsing
By default fig ignores any fields in the config file that are not present in the struct. This behaviour can be changed using `UseStrict()` to achieve strict parsing.
When strict parsing is enabled, extra fields in the config file will cause an error.
Required
A validate key with a required value in the field's struct tag makes fig check if the field has been set after it's been loaded. Required fields that are not set are returned as an error.
Expand Down
2 changes: 2 additions & 0 deletions fig.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type fig struct {
tag string
timeLayout string
useEnv bool
useStrict bool
ignoreFile bool
envPrefix string
}
Expand Down Expand Up @@ -156,6 +157,7 @@ func (f *fig) decodeMap(m map[string]interface{}, result interface{}) error {
WeaklyTypedInput: true,
Result: result,
TagName: f.tag,
ErrorUnused: f.useStrict,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToTimeHookFunc(f.timeLayout),
Expand Down
34 changes: 34 additions & 0 deletions fig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strings"
"testing"
"time"

"github.com/mitchellh/mapstructure"
)

type Pod struct {
Expand Down Expand Up @@ -344,6 +346,38 @@ func Test_fig_Load_RequiredAndDefaults(t *testing.T) {
}
}

func Test_fig_Load_UseStrict(t *testing.T) {
for _, f := range []string{"server.yaml", "server.json", "server.toml"} {
t.Run(f, func(t *testing.T) {
type Server struct {
Host string `fig:"host"`
}

var cfg Server
err := Load(&cfg, UseStrict(), File(f), Dirs(filepath.Join("testdata", "valid")))
if err == nil {
t.Fatalf("expected err")
}

want := []string{
"has invalid keys: logger",
}

fieldErrs := err.(*mapstructure.Error)

if len(want) != len(fieldErrs.Errors) {
t.Fatalf("\nlen(fieldErrs) != %d\ngot %+v\n", len(want), fieldErrs)
}

for i, err := range fieldErrs.Errors {
if !strings.Contains(err, want[i]) {
t.Errorf("want %s in fieldErrs, got %+v", want[i], err)
}
}
})
}
}

func Test_fig_Load_WithOptions(t *testing.T) {
for _, f := range []string{"server.yaml", "server.json", "server.toml"} {
t.Run(f, func(t *testing.T) {
Expand Down
13 changes: 13 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,16 @@ func UseEnv(prefix string) Option {
f.envPrefix = prefix
}
}

// UseStrict returns an option that configures fig to return an error if
// there exists additional fields in the config file that are not defined
// in the config struct.
//
// fig.Load(&cfg, fig.UseStrict())
//
// If this option is not used then fig ignores any additional fields in the config file.
func UseStrict() Option {
return func(f *fig) {
f.useStrict = true
}
}

0 comments on commit b6a316a

Please sign in to comment.