Skip to content

Commit

Permalink
feat: add support for parsing config from embed.FS
Browse files Browse the repository at this point in the history
  • Loading branch information
aranw committed May 23, 2024
1 parent 0057c95 commit 8569863
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 8 deletions.
26 changes: 24 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,37 @@ package yamlcfg

import (
"bytes"
"embed"
"fmt"
"os"

"gopkg.in/yaml.v3"
)

// Load takes the given path and attempts to read and unmarshal the config
// ParseFS attempts to load the given path and config from a embed.FS
func ParseFS[T any](fs embed.FS, path string) (*T, error) {
var cfg *T

b, err := fs.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading config from embed.FS: %w", err)
} else if err := UnmarshalConfig(&cfg, b); err != nil {
return nil, fmt.Errorf("unmarshalling config: %w", err)
}
if cfg, ok := interface{}(cfg).(interface {
Validate() error
}); ok {
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("validating config: %w", err)
}
}

return cfg, nil
}

// Parse takes the given path and attempts to read and unmarshal the config
// The given config will also be validated if it has a Validate function on it
func Load[T any](path string) (*T, error) {
func Parse[T any](path string) (*T, error) {
var cfg *T

b, err := os.ReadFile(path)
Expand Down
47 changes: 42 additions & 5 deletions config_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package yamlcfg

import (
"embed"
"fmt"
"testing"

Expand All @@ -23,10 +24,46 @@ func (t *TestStructWithFailingValidation) Validate() error {
return fmt.Errorf("this is going to fail")
}

func TestLoad(t *testing.T) {
//go:embed testdata/*
var testdata embed.FS

func TestParseFS(t *testing.T) {

t.Run("successfully load and unmarshals config", func(t *testing.T) {
cfg, err := ParseFS[TestStruct](testdata, "testdata/test1.yaml")
if err != nil {
t.Fatal(err)
}

qt.Assert(t, qt.Equals(cfg.SomeValue, "this is for testing purposes"))
})

t.Run("fails to read unknown file", func(t *testing.T) {
cfg, err := ParseFS[TestStruct](testdata, "testdata/this_file_does_not_exist.yaml")
qt.Assert(t, qt.IsNotNil(err))
qt.Assert(t, qt.ErrorMatches(err, "reading config from embed.FS: .*"))
qt.Assert(t, qt.IsNil(cfg))
})

t.Run("fails to read wrong file type", func(t *testing.T) {
cfg, err := ParseFS[TestStruct](testdata, "testdata/gopher.png")
qt.Assert(t, qt.IsNotNil(err))
qt.Assert(t, qt.ErrorMatches(err, "unmarshalling config: yaml: .*"))
qt.Assert(t, qt.IsNil(cfg))
})

t.Run("fails to validate config struct", func(t *testing.T) {
cfg, err := ParseFS[TestStructWithFailingValidation](testdata, "testdata/test1.yaml")
qt.Assert(t, qt.IsNotNil(err))
qt.Assert(t, qt.ErrorMatches(err, "validating config: this is going to fail"))
qt.Assert(t, qt.IsNil(cfg))
})
}

func TestParse(t *testing.T) {

t.Run("successfully load and unmarshals config", func(t *testing.T) {
cfg, err := Load[TestStruct]("testdata/test1.yaml")
cfg, err := Parse[TestStruct]("testdata/test1.yaml")
if err != nil {
t.Fatal(err)
}
Expand All @@ -35,21 +72,21 @@ func TestLoad(t *testing.T) {
})

t.Run("fails to read unknown file", func(t *testing.T) {
cfg, err := Load[TestStruct]("testdata/this_file_does_not_exist.yaml")
cfg, err := Parse[TestStruct]("testdata/this_file_does_not_exist.yaml")
qt.Assert(t, qt.IsNotNil(err))
qt.Assert(t, qt.ErrorMatches(err, "reading config file: .*"))
qt.Assert(t, qt.IsNil(cfg))
})

t.Run("fails to read wrong file type", func(t *testing.T) {
cfg, err := Load[TestStruct]("testdata/gopher.png")
cfg, err := Parse[TestStruct]("testdata/gopher.png")
qt.Assert(t, qt.IsNotNil(err))
qt.Assert(t, qt.ErrorMatches(err, "unmarshalling config: yaml: .*"))
qt.Assert(t, qt.IsNil(cfg))
})

t.Run("fails to validate config struct", func(t *testing.T) {
cfg, err := Load[TestStructWithFailingValidation]("testdata/test1.yaml")
cfg, err := Parse[TestStructWithFailingValidation]("testdata/test1.yaml")
qt.Assert(t, qt.IsNotNil(err))
qt.Assert(t, qt.ErrorMatches(err, "validating config: this is going to fail"))
qt.Assert(t, qt.IsNil(cfg))
Expand Down
2 changes: 1 addition & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Config struct {
}

func ExampleLoad() {
cfg, err := yamlcfg.Load[Config]("config.yaml")
cfg, err := yamlcfg.Parse[Config]("config.yaml")
if err != nil {
slog.Error("loading yaml config", "err", err)
return
Expand Down

0 comments on commit 8569863

Please sign in to comment.