Skip to content

Commit

Permalink
add location to parse errors
Browse files Browse the repository at this point in the history
Fixes #11
  • Loading branch information
speter committed Oct 13, 2017
1 parent 97a003b commit 298b7a6
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 22 deletions.
36 changes: 26 additions & 10 deletions errors.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package gcfg

import (
"gopkg.in/warnings.v0"
)
import warnings "gopkg.in/warnings.v0"

// FatalOnly filters the results of a Read*Into invocation and returns only
// fatal errors. That is, errors (warnings) indicating data for unknown
Expand All @@ -21,21 +19,39 @@ func isFatal(err error) bool {
return !ok
}

type extraData struct {
type loc struct {
section string
subsection *string
variable *string
}

func (e extraData) Error() string {
s := "can't store data at section \"" + e.section + "\""
if e.subsection != nil {
s += ", subsection \"" + *e.subsection + "\""
type extraData struct {
loc
}

type locErr struct {
msg string
loc
}

func (l loc) String() string {
s := "section \"" + l.section + "\""
if l.subsection != nil {
s += ", subsection \"" + *l.subsection + "\""
}
if e.variable != nil {
s += ", variable \"" + *e.variable + "\""
if l.variable != nil {
s += ", variable \"" + *l.variable + "\""
}
return s
}

func (e extraData) Error() string {
return "can't store data at " + e.loc.String()
}

func (e locErr) Error() string {
return e.msg + " at " + e.loc.String()
}

var _ error = extraData{}
var _ error = locErr{}
31 changes: 31 additions & 0 deletions issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,34 @@ func TestGoogleCodeIssue2(t *testing.T) {
testRead(t, id, tt)
}
}

type ConfigIssue11 struct {
Sect struct {
Var bool
}
}

var testsIssue11 = []struct {
cfg string
loc string
}{
{"[Sect]\nVar=X", "Sect"},
{"[Sect]\nVar=X", "Var"},
}

// Value parse error should include location
func TestIssue11(t *testing.T) {
for i, tt := range testsIssue11 {
var c ConfigIssue11
err := ReadStringInto(&c, tt.cfg)
switch {
case err == nil:
t.Errorf("%d fail: got ok; wanted error", i)
case !strings.Contains(err.Error(), tt.loc):
t.Errorf("%d fail: error message doesn't contain location %q: %v",
i, tt.loc, err)
default:
t.Logf("%d pass: %v", i, err)
}
}
}
20 changes: 8 additions & 12 deletions set.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,17 @@ func set(c *warnings.Collector, cfg interface{}, sect, sub, name string,
}
vCfg := vPCfg.Elem()
vSect, _ := fieldFold(vCfg, sect)
l := loc{section: sect}
if !vSect.IsValid() {
err := extraData{section: sect}
err := extraData{loc: l}
return c.Collect(err)
}
isSubsect := vSect.Kind() == reflect.Map
if subsectPass != isSubsect {
return nil
}
if isSubsect {
l.subsection = &sub
vst := vSect.Type()
if vst.Key().Kind() != reflect.String ||
vst.Elem().Kind() != reflect.Ptr ||
Expand All @@ -256,23 +258,17 @@ func set(c *warnings.Collector, cfg interface{}, sect, sub, name string,
panic(fmt.Errorf("field for section must be a map or a struct: "+
"section %q", sect))
} else if sub != "" {
err := extraData{section: sect, subsection: &sub}
return c.Collect(err)
return c.Collect(extraData{loc: l})
}
// Empty name is a special value, meaning that only the
// section/subsection object is to be created, with no values set.
if name == "" {
return nil
}
vVar, t := fieldFold(vSect, name)
l.variable = &name
if !vVar.IsValid() {
var err error
if isSubsect {
err = extraData{section: sect, subsection: &sub, variable: &name}
} else {
err = extraData{section: sect, variable: &name}
}
return c.Collect(err)
return c.Collect(extraData{loc: l})
}
// vVal is either single-valued var, or newly allocated value within multi-valued var
var vVal reflect.Value
Expand Down Expand Up @@ -315,12 +311,12 @@ func set(c *warnings.Collector, cfg interface{}, sect, sub, name string,
break
}
if err != errUnsupportedType {
return err
return locErr{msg: err.Error(), loc: l}
}
}
if !ok {
// in case all setters returned errUnsupportedType
return err
return locErr{msg: err.Error(), loc: l}
}
if isNew { // set reference if it was dereferenced and newly allocated
vVal.Set(vAddr)
Expand Down

0 comments on commit 298b7a6

Please sign in to comment.