All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
121 lines
2.9 KiB
Go
121 lines
2.9 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Config holds weatherapi datasource configuration.
|
|
type Config struct {
|
|
Databases []DatabaseConfig
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
Name string `yaml:"name"`
|
|
Driver string `yaml:"driver"`
|
|
Params DatabaseParams `yaml:"params"`
|
|
}
|
|
|
|
type DatabaseParams struct {
|
|
URI string `yaml:"uri"`
|
|
Username string `yaml:"username"`
|
|
Password string `yaml:"password"`
|
|
}
|
|
|
|
type configWrapper struct {
|
|
Databases []DatabaseConfig `yaml:"databases"`
|
|
}
|
|
|
|
func Load(path string) (*Config, error) {
|
|
if strings.TrimSpace(path) == "" {
|
|
path = "config.yml"
|
|
}
|
|
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("config.Load: read %q: %w", path, err)
|
|
}
|
|
|
|
var list []DatabaseConfig
|
|
if err := decodeStrict(raw, &list); err == nil && len(list) > 0 {
|
|
cfg := &Config{Databases: list}
|
|
if err := cfg.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
var wrapped configWrapper
|
|
if err := decodeStrict(raw, &wrapped); err != nil {
|
|
return nil, fmt.Errorf("config.Load: parse YAML %q: %w", path, err)
|
|
}
|
|
|
|
cfg := &Config{Databases: wrapped.Databases}
|
|
if err := cfg.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func decodeStrict(raw []byte, out any) error {
|
|
dec := yaml.NewDecoder(strings.NewReader(string(raw)))
|
|
dec.KnownFields(true)
|
|
if err := dec.Decode(out); err != nil {
|
|
return err
|
|
}
|
|
|
|
var extra any
|
|
if err := dec.Decode(&extra); err == nil {
|
|
return fmt.Errorf("contains multiple YAML documents; expected exactly one")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) Validate() error {
|
|
if c == nil {
|
|
return fmt.Errorf("config validation failed: config is nil")
|
|
}
|
|
if len(c.Databases) == 0 {
|
|
return fmt.Errorf("config validation failed: no databases configured")
|
|
}
|
|
|
|
seen := map[string]struct{}{}
|
|
for i, db := range c.Databases {
|
|
path := fmt.Sprintf("databases[%d]", i)
|
|
if strings.TrimSpace(db.Name) == "" {
|
|
return fmt.Errorf("config validation failed: %s.name is required", path)
|
|
}
|
|
if _, ok := seen[db.Name]; ok {
|
|
return fmt.Errorf("config validation failed: %s.name %q is duplicated", path, db.Name)
|
|
}
|
|
seen[db.Name] = struct{}{}
|
|
|
|
if strings.TrimSpace(db.Driver) == "" {
|
|
return fmt.Errorf("config validation failed: %s.driver is required", path)
|
|
}
|
|
if strings.TrimSpace(db.Params.URI) == "" {
|
|
return fmt.Errorf("config validation failed: %s.params.uri is required", path)
|
|
}
|
|
if strings.TrimSpace(db.Params.Username) == "" {
|
|
return fmt.Errorf("config validation failed: %s.params.username is required", path)
|
|
}
|
|
if strings.TrimSpace(db.Params.Password) == "" {
|
|
return fmt.Errorf("config validation failed: %s.params.password is required", path)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) FindDatabase(name string) (DatabaseConfig, bool) {
|
|
for _, db := range c.Databases {
|
|
if db.Name == name {
|
|
return db, true
|
|
}
|
|
}
|
|
return DatabaseConfig{}, false
|
|
}
|