7 changed files with 227 additions and 0 deletions
@ -0,0 +1,31 @@ |
|||
title = "TOML Example" |
|||
|
|||
[owner] |
|||
name = "Tom Preston-Werner" |
|||
dob = 1979-05-27T07:32:00-08:00 # First class dates |
|||
|
|||
[database] |
|||
server = "192.168.1.1" |
|||
ports = [ 8001, 8001, 8002 ] |
|||
connection_max = 5000 |
|||
enabled = true |
|||
|
|||
[servers] |
|||
|
|||
# Indentation (tabs and/or spaces) is allowed but not required |
|||
[servers.alpha] |
|||
ip = "10.0.0.1" |
|||
dc = "eqdc10" |
|||
|
|||
[servers.beta] |
|||
ip = "10.0.0.2" |
|||
dc = "eqdc10" |
|||
|
|||
[clients] |
|||
data = [ ["gamma", "delta"], [1, 2] ] |
|||
|
|||
# Line breaks are OK when inside arrays |
|||
hosts = [ |
|||
"alpha", |
|||
"omega" |
|||
] |
@ -0,0 +1,15 @@ |
|||
module gitoa.ru/go-4devs/config/provider/toml |
|||
|
|||
go 1.21 |
|||
|
|||
require ( |
|||
github.com/pelletier/go-toml v1.9.5 |
|||
github.com/stretchr/testify v1.8.4 |
|||
gitoa.ru/go-4devs/config v0.0.1 |
|||
) |
|||
|
|||
require ( |
|||
github.com/davecgh/go-spew v1.1.1 // indirect |
|||
github.com/pmezard/go-difflib v1.0.0 // indirect |
|||
gopkg.in/yaml.v3 v3.0.1 // indirect |
|||
) |
@ -0,0 +1,14 @@ |
|||
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/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= |
|||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= |
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
|||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= |
|||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= |
|||
gitoa.ru/go-4devs/config v0.0.1 h1:9KrOO09YbIMO8qL8aVn/G74DurGdOIW5y3O02bays4I= |
|||
gitoa.ru/go-4devs/config v0.0.1/go.mod h1:xfEC2Al9xnMLJUuekYs3KhJ5BIzWAseNwkMwbN6/xss= |
|||
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= |
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
@ -0,0 +1,71 @@ |
|||
package toml |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"strings" |
|||
|
|||
"github.com/pelletier/go-toml" |
|||
"gitoa.ru/go-4devs/config" |
|||
"gitoa.ru/go-4devs/config/value" |
|||
) |
|||
|
|||
const ( |
|||
Name = "toml" |
|||
Separator = "." |
|||
) |
|||
|
|||
var _ config.Provider = (*Provider)(nil) |
|||
|
|||
func NewFile(file string, opts ...Option) (*Provider, error) { |
|||
tree, err := toml.LoadFile(file) |
|||
if err != nil { |
|||
return nil, fmt.Errorf("toml: failed load file: %w", err) |
|||
} |
|||
|
|||
return configure(tree, opts...), nil |
|||
} |
|||
|
|||
type Option func(*Provider) |
|||
|
|||
func configure(tree *toml.Tree, opts ...Option) *Provider { |
|||
prov := &Provider{ |
|||
tree: tree, |
|||
key: func(s []string) string { |
|||
return strings.Join(s, Separator) |
|||
}, |
|||
} |
|||
|
|||
for _, opt := range opts { |
|||
opt(prov) |
|||
} |
|||
|
|||
return prov |
|||
} |
|||
|
|||
func New(data []byte, opts ...Option) (*Provider, error) { |
|||
tree, err := toml.LoadBytes(data) |
|||
if err != nil { |
|||
return nil, fmt.Errorf("toml failed load data: %w", err) |
|||
} |
|||
|
|||
return configure(tree, opts...), nil |
|||
} |
|||
|
|||
type Provider struct { |
|||
tree *toml.Tree |
|||
key func([]string) string |
|||
name string |
|||
} |
|||
|
|||
func (p *Provider) Name() string { |
|||
return p.name |
|||
} |
|||
|
|||
func (p *Provider) Value(_ context.Context, path ...string) (config.Value, error) { |
|||
if k := p.key(path); p.tree.Has(k) { |
|||
return Value{Value: value.Value{Val: p.tree.Get(k)}}, nil |
|||
} |
|||
|
|||
return nil, config.ErrValueNotFound |
|||
} |
@ -0,0 +1,36 @@ |
|||
package toml_test |
|||
|
|||
import ( |
|||
"embed" |
|||
"testing" |
|||
|
|||
"gitoa.ru/go-4devs/config/provider/toml" |
|||
"gitoa.ru/go-4devs/config/test" |
|||
"gitoa.ru/go-4devs/config/test/require" |
|||
) |
|||
|
|||
//go:embed fixture/*
|
|||
var fixtures embed.FS |
|||
|
|||
func TestProvider(t *testing.T) { |
|||
t.Parallel() |
|||
|
|||
files, ferr := fixtures.ReadFile("fixture/config.toml") |
|||
require.NoError(t, ferr) |
|||
|
|||
prov, err := toml.New(files) |
|||
require.NoError(t, err) |
|||
|
|||
m := []int{} |
|||
|
|||
read := []test.Read{ |
|||
test.NewRead("192.168.1.1", "database.server"), |
|||
test.NewRead("TOML Example", "title"), |
|||
test.NewRead("10.0.0.1", "servers.alpha.ip"), |
|||
test.NewRead(true, "database.enabled"), |
|||
test.NewRead(5000, "database.connection_max"), |
|||
test.NewReadUnmarshal(&[]int{8001, 8001, 8002}, &m, "database", "ports"), |
|||
} |
|||
|
|||
test.Run(t, prov, read) |
|||
} |
@ -0,0 +1,41 @@ |
|||
package toml |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
|
|||
"gitoa.ru/go-4devs/config" |
|||
"gitoa.ru/go-4devs/config/value" |
|||
) |
|||
|
|||
type Value struct { |
|||
value.Value |
|||
} |
|||
|
|||
func (s Value) Int() int { |
|||
v, _ := s.ParseInt() |
|||
|
|||
return v |
|||
} |
|||
|
|||
func (s Value) ParseInt() (int, error) { |
|||
v, err := s.ParseInt64() |
|||
if err != nil { |
|||
return 0, fmt.Errorf("toml failed parce int: %w", err) |
|||
} |
|||
|
|||
return int(v), nil |
|||
} |
|||
|
|||
func (s Value) Unmarshal(target interface{}) error { |
|||
b, err := json.Marshal(s.Raw()) |
|||
if err != nil { |
|||
return fmt.Errorf("%w: %w", config.ErrInvalidValue, err) |
|||
} |
|||
|
|||
if err := json.Unmarshal(b, target); err != nil { |
|||
return fmt.Errorf("%w: %w", config.ErrInvalidValue, err) |
|||
} |
|||
|
|||
return nil |
|||
} |
Loading…
Reference in new issue