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