Document and demonstrate native and layered configuration loading
This commit is contained in:
95
examples/consumers/layered.go
Normal file
95
examples/consumers/layered.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package consumers
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"path"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
)
|
||||
|
||||
//go:embed testdata/native-source testdata/layered-bundle
|
||||
var bundledFiles embed.FS
|
||||
|
||||
// BundledConfig is a small configuration loaded directly from an embedded file.
|
||||
type BundledConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
// LoadBundled demonstrates a native filesystem source without layered options.
|
||||
func LoadBundled() (*configkit.Result[BundledConfig], error) {
|
||||
files, err := fs.Sub(bundledFiles, "testdata/native-source")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configkit.Load[BundledConfig](configkit.NewFilesystem("bundled", files).File("config.yml"))
|
||||
}
|
||||
|
||||
// CatalogConfig keeps resource names and domain rules in the application.
|
||||
type CatalogConfig struct {
|
||||
Mode string `yaml:"mode"`
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Workers int `yaml:"workers"`
|
||||
Templates map[string]string `yaml:"templates"`
|
||||
Labels map[string]string `yaml:"labels"`
|
||||
}
|
||||
|
||||
// LoadCatalog combines two underlay tiers beneath an independently located
|
||||
// host main file. ConfigKit assembles and validates the complete result once.
|
||||
func LoadCatalog(mainPath string) (*configkit.Result[CatalogConfig], error) {
|
||||
files, err := fs.Sub(bundledFiles, "testdata/layered-bundle")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bundle := configkit.NewFilesystem("catalog bundle", files)
|
||||
return configkit.LoadLayered(configkit.Input{Source: configkit.File(mainPath)},
|
||||
configkit.LayeredOptions[CatalogConfig]{
|
||||
Underlays: []configkit.Underlay{
|
||||
{Inputs: []configkit.Input{{Source: bundle.File("defaults/config.yml"), Imports: true}}},
|
||||
{Inputs: []configkit.Input{{Source: configkit.Mapping("local policy", map[string]any{
|
||||
"workers": 2,
|
||||
"labels": map[string]any{"tenant": "example"},
|
||||
})}}},
|
||||
},
|
||||
Definition: configkit.Definition[CatalogConfig]{
|
||||
Defaults: func() CatalogConfig { return CatalogConfig{Mode: "demo"} },
|
||||
Validate: validateCatalog,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func validateCatalog(configuration *CatalogConfig, validation *configkit.Validation) {
|
||||
if configuration.Mode != "demo" && configuration.Mode != "production" {
|
||||
validation.Error(configkit.Keys("mode"), "must be demo or production")
|
||||
}
|
||||
if configuration.Workers < 0 {
|
||||
validation.Error(configkit.Keys("workers"), "must be nonnegative")
|
||||
}
|
||||
if name := configuration.Templates["welcome"]; name == "." || !fs.ValidPath(name) {
|
||||
validation.Error(configkit.Keys("templates", "welcome"), "must be a relative resource name")
|
||||
}
|
||||
}
|
||||
|
||||
// ReadWelcome follows the effective template field's one declaring source to
|
||||
// its retained resource context. Opening and interpreting the resource remain
|
||||
// application work; this does not reopen configuration or change metadata.
|
||||
func ReadWelcome(result *configkit.Result[CatalogConfig]) (string, error) {
|
||||
if result == nil {
|
||||
return "", errors.New("catalog result is nil")
|
||||
}
|
||||
info, found := result.Metadata().Lookup(configkit.Keys("templates", "welcome"))
|
||||
if !found || len(info.Origin.Locations) != 1 {
|
||||
return "", errors.New("welcome template has no single declaring source")
|
||||
}
|
||||
context, found := result.SourceContext(info.Origin.Locations[0].SourceID)
|
||||
if !found {
|
||||
return "", errors.New("welcome template has no resource context")
|
||||
}
|
||||
data, err := fs.ReadFile(context.FS, path.Join(context.BaseDir, result.Config.Templates["welcome"]))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
Reference in New Issue
Block a user