diff --git a/config.go b/config.go index ce21764..de9867d 100644 --- a/config.go +++ b/config.go @@ -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) diff --git a/config_test.go b/config_test.go index 931725d..5633e04 100644 --- a/config_test.go +++ b/config_test.go @@ -1,6 +1,7 @@ package yamlcfg import ( + "embed" "fmt" "testing" @@ -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) } @@ -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)) diff --git a/example_test.go b/example_test.go index 3a5863b..a81e4a2 100644 --- a/example_test.go +++ b/example_test.go @@ -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