Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ func main() {
}
```
Those functions triggered inside `ConfigParser.Get*` methods if presented and wraps the return value.
> NOTE: Since `ConvertFunc` returns `any`, the caller should guarantee type assertion to the requested type after custom processing!
```go
type Converter map[string]ConvertFunc
```
Expand Down
57 changes: 39 additions & 18 deletions methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,26 +93,34 @@ func (p *ConfigParser) Get(section, option string) (string, error) {
return "", err
}

return value.(string), nil
return assertValue[string](value)
}

func (p *ConfigParser) get(section, option string) (string, error) {
if !p.HasSection(section) {
if !p.isDefaultSection(section) {
return "", getNoSectionError(section)
}
if value, err := p.defaults.Get(option); err != nil {
return "", getNoOptionError(section, option)
} else {
return value, nil
}
} else if value, err := p.config[section].Get(option); err == nil {
return value, nil
} else if value, err := p.defaults.Get(option); err == nil {
return value, nil
// Special case.
if p.isDefaultSection(section) {
return p.defaults.Get(option)
}

s, ok := p.config[section]
if !ok {
return "", getNoSectionError(section)
}

v, err := s.Get(option)
if err == nil {
return v, nil
}

return "", getNoOptionError(section, option)
// If given section has no option, fallback to check default.
dv, derr := p.defaults.Get(option)
if derr != nil {
// If option is not present in default section, return
// original error with the requested section name.
return "", err
}

return dv, nil
}

// ItemsWithDefaults returns a copy of the named section Dict including
Expand Down Expand Up @@ -185,7 +193,7 @@ func (p *ConfigParser) GetInt64(section, option string) (int64, error) {
return 0, err
}

return value.(int64), nil
return assertValue[int64](value)
}

// GetFloat64 returns float64 representation of the named option.
Expand All @@ -204,7 +212,7 @@ func (p *ConfigParser) GetFloat64(section, option string) (float64, error) {
return 0, err
}

return value.(float64), nil
return assertValue[float64](value)
}

// GetBool returns bool representation of the named option.
Expand All @@ -223,7 +231,7 @@ func (p *ConfigParser) GetBool(section, option string) (bool, error) {
return false, err
}

return value.(bool), nil
return assertValue[bool](value)
}

// RemoveSection removes given section from the ConfigParser.
Expand Down Expand Up @@ -316,3 +324,16 @@ func defaultGetBool(value string) (any, error) {

return booleanValue, nil
}

// assertValue tries value assertion to the given type, returns error if unsuccessful.
func assertValue[T ~string | ~int64 | ~float64 | ~bool](value any) (T, error) {
v, ok := value.(T)
if ok {
return v, nil
}

// Default value of the type.
var d T

return d, fmt.Errorf("assertion to %T failed: incorrect value %q", d, value)
}
26 changes: 26 additions & 0 deletions methods_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package configparser_test

import (
"strings"

"github.com/bigkevmcd/go-configparser"

gc "gopkg.in/check.v1"
Expand Down Expand Up @@ -368,3 +370,27 @@ func (s *ConfigParserSuite) TestOptionsWithSectionStripsWhitespaceFromKeys(c *gc
c.Assert(err, gc.IsNil)
c.Assert(result, gc.DeepEquals, []string{"base_dir", "bin_dir", "foo"})
}

func brokenConv(_ string) (any, error) { return nil, nil }

// Get* methods must fail with a proper error instead of a panic if type assertion has failed.
func (s *ConfigParserSuite) TestGetWithAssertionError(c *gc.C) {
parser, err := configparser.ParseReaderWithOptions(
strings.NewReader("[testing]\nstring=new string\nint=200\nfloat=3.14159265\nbool=TRUE"),
configparser.Converters(configparser.Converter{
configparser.StringConv: brokenConv,
configparser.IntConv: brokenConv,
configparser.FloatConv: brokenConv,
configparser.BoolConv: brokenConv,
}),
)
c.Assert(err, gc.IsNil)
_, err = parser.Get("testing", "string")
c.Assert(err, gc.ErrorMatches, ".*assertion to string failed.*")
_, err = parser.GetInt64("testing", "int")
c.Assert(err, gc.ErrorMatches, ".*assertion to int64 failed.*")
_, err = parser.GetFloat64("testing", "float")
c.Assert(err, gc.ErrorMatches, ".*assertion to float64 failed.*")
_, err = parser.GetBool("testing", "bool")
c.Assert(err, gc.ErrorMatches, ".*assertion to bool failed.*")
}
6 changes: 3 additions & 3 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ type options struct {
}

// Converter contains custom convert functions for available types.
// The caller should guarantee type assertion to the requested type
// after custom processing!
type Converter map[int]ConvertFunc

// Predefined types for Converter.
Expand Down Expand Up @@ -145,7 +143,9 @@ func Delimiters(d string) optFunc {
// after custom processing or the method will panic.
func Converters(conv Converter) optFunc {
return func(o *options) {
o.converters = conv
for k, fn := range conv {
o.converters[k] = fn
}
}
}

Expand Down