feat: add support for ini files
This commit is contained in:
parent
89e6dbfb98
commit
fdf99daa9c
12 changed files with 94 additions and 4 deletions
64
adapters.go
64
adapters.go
|
@ -1,16 +1,20 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"gopkg.in/ini.v1"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
@ -26,6 +30,7 @@ const (
|
|||
typeTOML
|
||||
typeHCL
|
||||
typeYAML
|
||||
typeINI
|
||||
)
|
||||
|
||||
// getType returns the type of the config file.
|
||||
|
@ -40,6 +45,9 @@ func getType(filename string) filetype {
|
|||
case strings.HasSuffix(filename, ".yml") ||
|
||||
strings.HasSuffix(filename, ".yaml"):
|
||||
return typeYAML
|
||||
case strings.HasSuffix(filename, ".ini") ||
|
||||
strings.HasSuffix(filename, ".cfg"):
|
||||
return typeINI
|
||||
default:
|
||||
return typeInvalid
|
||||
}
|
||||
|
@ -58,6 +66,8 @@ func unmarshal(filepath string, data []byte, v any) error {
|
|||
return unmarshalHCL(filepath, data, v)
|
||||
case typeYAML:
|
||||
return unmarshalYAML(data, v)
|
||||
case typeINI:
|
||||
return unmarshalINI(data, v)
|
||||
default:
|
||||
return ErrUnsupportedFileType
|
||||
}
|
||||
|
@ -74,6 +84,8 @@ func marshal(ft filetype, v any) ([]byte, error) {
|
|||
return marshalHCL(v)
|
||||
case typeYAML:
|
||||
return marshalYAML(v)
|
||||
case typeINI:
|
||||
return marshalINI(v)
|
||||
default:
|
||||
return nil, ErrUnsupportedFileType
|
||||
}
|
||||
|
@ -178,3 +190,55 @@ func marshalYAML(v any) ([]byte, error) {
|
|||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// unmarshalINI unmarshals the given data to the given struct.
|
||||
func unmarshalINI(data []byte, v any) error {
|
||||
opts := ini.LoadOptions{}
|
||||
cfg, err := ini.LoadSources(opts, data)
|
||||
if err != nil {
|
||||
return parseError(err)
|
||||
}
|
||||
|
||||
cfg.NameMapper = iniNameMapper
|
||||
|
||||
err = cfg.MapTo(v)
|
||||
if err != nil {
|
||||
return parseError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// marshalYAML marshals the given struct to bytes.
|
||||
func marshalINI(v any) ([]byte, error) {
|
||||
cfg := ini.Empty()
|
||||
|
||||
val := reflect.ValueOf(v)
|
||||
if val.Kind() != reflect.Ptr {
|
||||
v = &v
|
||||
}
|
||||
|
||||
err := ini.ReflectFromWithMapper(cfg, v, iniNameMapper)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot generate config content: %w", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cfg.WriteTo(&buf)
|
||||
data := buf.Bytes()
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func iniNameMapper(raw string) string {
|
||||
newstr := make([]rune, 0, len(raw))
|
||||
for i, chr := range raw {
|
||||
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
|
||||
if i > 0 {
|
||||
newstr = append(newstr, '_')
|
||||
}
|
||||
}
|
||||
newstr = append(newstr, unicode.ToLower(chr))
|
||||
}
|
||||
return string(newstr)
|
||||
}
|
||||
|
|
|
@ -35,6 +35,12 @@ func TestYAMLFiles(t *testing.T) {
|
|||
runTestSuite(t, "yaml")
|
||||
}
|
||||
|
||||
func TestINIFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runTestSuite(t, "ini")
|
||||
}
|
||||
|
||||
func TestUnknownFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
7
go.mod
7
go.mod
|
@ -2,11 +2,12 @@ module code.bcarlin.net/go/conf
|
|||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.23.4
|
||||
|
||||
require (
|
||||
github.com/hashicorp/hcl/v2 v2.23.0
|
||||
github.com/pelletier/go-toml/v2 v2.2.3
|
||||
github.com/stretchr/testify v1.10.0
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -15,7 +16,6 @@ require (
|
|||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.23.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/zclconf/go-cty v1.13.0 // indirect
|
||||
|
@ -23,5 +23,4 @@ require (
|
|||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
8
go.sum
8
go.sum
|
@ -6,6 +6,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew
|
|||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
|
||||
|
@ -20,8 +22,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
|
||||
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
|
@ -30,5 +36,7 @@ golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
|||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
3
test_data/full.ini
Normal file
3
test_data/full.ini
Normal file
|
@ -0,0 +1,3 @@
|
|||
string = default string
|
||||
invariant = should not change
|
||||
int = 1
|
1
test_data/invalid.ini
Normal file
1
test_data/invalid.ini
Normal file
|
@ -0,0 +1 @@
|
|||
String not ini
|
1
test_data/part1.ini
Normal file
1
test_data/part1.ini
Normal file
|
@ -0,0 +1 @@
|
|||
string = foo
|
1
test_data/part2.ini
Normal file
1
test_data/part2.ini
Normal file
|
@ -0,0 +1 @@
|
|||
int = 42
|
1
test_data/same1.ini
Normal file
1
test_data/same1.ini
Normal file
|
@ -0,0 +1 @@
|
|||
string = foo
|
1
test_data/same2.ini
Normal file
1
test_data/same2.ini
Normal file
|
@ -0,0 +1 @@
|
|||
string = bar
|
3
test_data/unknown.ini
Normal file
3
test_data/unknown.ini
Normal file
|
@ -0,0 +1,3 @@
|
|||
string = config string
|
||||
int = 42
|
||||
unknown = foo
|
2
test_data/valid.ini
Normal file
2
test_data/valid.ini
Normal file
|
@ -0,0 +1,2 @@
|
|||
string = config string
|
||||
int = 42
|
Loading…
Add table
Reference in a new issue