♻️ Refactor config

Make it easier to setup stores
This commit is contained in:
Dan Jones 2025-01-26 20:07:45 -06:00
commit 6f06adc37d
12 changed files with 623 additions and 81 deletions

View file

@ -1,18 +1,76 @@
package config
type Config struct {
Name string `toml:"name"`
Env Env `toml:"env"`
BaseURL string `toml:"base_url"`
Conn ConnSettings `toml:"conn"`
import (
"errors"
"github.com/BurntSushi/toml"
)
type config struct {
name string
env Env
baseURL string
stores stores
md toml.MetaData
}
type ConnSettings struct {
Store string `toml:"store"`
DSN string `toml:"dsn"`
Settings map[string]any `toml:"settings"`
var _ Config = config{}
func (c config) Name() string {
return c.name
}
func (c Config) Environment() Env {
return ValidEnvOrDev(c.Env)
func (c config) Env() Env {
return ValidEnvOrDev(c.env)
}
func (c config) BaseURL() string {
return c.baseURL
}
func (c config) Store(name string) (Store, error) {
if name == "" {
name = c.stores.store
}
return c.stores.GetStore(name)
}
func (c config) StoreName() string {
return c.stores.store
}
type stores struct {
store string
settings map[string]toml.Primitive
conf *config
}
var ErrMissingStore = errors.New("unknown store")
func (ss stores) GetStore(name string) (Store, error) {
if tp, ok := ss.settings[name]; ok {
return store{name, tp, ss.conf.md}, nil
}
return nil, ErrMissingStore
}
type store struct {
name string
settings toml.Primitive
md toml.MetaData
}
var _ Store = store{}
func (s store) Name() string {
return s.name
}
func (s store) Map() (m map[string]any, err error) {
err = s.Decode(&m)
return
}
func (s store) Decode(v any) error {
return s.md.PrimitiveDecode(s.settings, v)
}

View file

@ -7,20 +7,20 @@ import (
)
func TestEnvDefaultsToDev(t *testing.T) {
c := Config{}
assert.Equal(t, Dev, c.Environment())
c := config{}
assert.Equal(t, Dev, c.Env())
}
func TestInvalidEnvReturnsDev(t *testing.T) {
c := Config{Env: Env("foobar")}
assert.Equal(t, Dev, c.Environment())
c := config{env: Env("foobar")}
assert.Equal(t, Dev, c.Env())
}
func TestValidEnvReturnsCorrect(t *testing.T) {
for _, e := range Envs {
t.Run(string(e), func(t *testing.T) {
c := Config{Env: e}
assert.Equal(t, e, c.Environment())
c := config{env: e}
assert.Equal(t, e, c.Env())
})
}
}

17
config/interface.go Normal file
View file

@ -0,0 +1,17 @@
package config
//go:generate mockgen -source interface.go -destination ../internal/testmocks/config/config_mock.go -package config -typed
type Config interface {
Name() string
Env() Env
BaseURL() string
StoreName() string
Store(name string) (Store, error)
}
type Store interface {
Name() string
Decode(v any) error
Map() (map[string]any, error)
}

View file

@ -2,7 +2,39 @@ package config
import "github.com/BurntSushi/toml"
func LoadFromToml(path string) (c Config, err error) {
_, err = toml.DecodeFile(path, &c)
return
type confToml struct {
Name string
Env Env
BaseURL string `toml:"base_url"`
Stores storeSettings `toml:"stores"`
}
type storeSettings struct {
Store string
Settings map[string]toml.Primitive
}
func LoadFromToml(path string) (Config, error) {
var c confToml
md, err := toml.DecodeFile(path, &c)
if err != nil {
return nil, err
}
conf := config{
name: c.Name,
env: c.Env,
baseURL: c.BaseURL,
md: md,
}
st := stores{
store: c.Stores.Store,
settings: c.Stores.Settings,
conf: &conf,
}
conf.stores = st
return conf, nil
}

View file

@ -13,18 +13,33 @@ func TestLoadTomlMissing(t *testing.T) {
assert.Error(t, e)
}
type sqlSett struct {
Path string
Num int
}
func TestLoadTomlGood(t *testing.T) {
tmp, _ := os.CreateTemp("", "*.toml")
defer os.Remove(tmp.Name())
defer tmp.Close()
fmt.Fprintln(tmp, `name = "Cool"`)
fmt.Fprintln(tmp, "[conn]")
fmt.Fprintln(tmp, "[stores]")
fmt.Fprintln(tmp, `store = "sqlite"`)
fmt.Fprintln(tmp, "[conn.settings]")
fmt.Fprintln(tmp, "[stores.settings.sqlite]")
fmt.Fprintln(tmp, `path = "tmp"`)
fmt.Fprintln(tmp, `num = 42`)
c, e := LoadFromToml(tmp.Name())
assert.NoError(t, e)
assert.Equal(t, "Cool", c.Name)
assert.Equal(t, "sqlite", c.Conn.Store)
assert.Equal(t, int64(42), c.Conn.Settings["num"])
assert.Equal(t, "Cool", c.Name())
st, err := c.Store("")
assert.NoError(t, err)
assert.Equal(t, "sqlite", st.Name())
var sett sqlSett
err = st.Decode(&sett)
assert.NoError(t, err)
assert.Equal(t, 42, sett.Num)
assert.Equal(t, "tmp", sett.Path)
}