package compass import ( "fmt" "os" "path/filepath" "strings" "github.com/knadh/koanf/parsers/yaml" "github.com/knadh/koanf/v2" ) // Config is a YAML configuration tree with dot-path access. type Config struct { k *koanf.Koanf } // Load reads config/
.yaml files from dir. Each filename stem becomes // the top-level section, so config/app.yaml key name is app.name. func Load(dir string) (*Config, error) { k := koanf.New(".") entries, err := os.ReadDir(dir) if err != nil { return nil, fmt.Errorf("compass: read config dir %s: %w", dir, err) } parser := yaml.Parser() for _, entry := range entries { if entry.IsDir() { continue } name := entry.Name() section, ok := sectionName(name) if !ok { continue } path := filepath.Join(dir, name) raw, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("compass: read %s: %w", path, err) } parsed, err := parser.Unmarshal(raw) if err != nil { return nil, fmt.Errorf("compass: parse %s: %w", path, err) } if parsed == nil { parsed = map[string]any{} } if err := k.Set(section, parsed); err != nil { return nil, fmt.Errorf("compass: set section %s: %w", section, err) } } return &Config{k: k}, nil } // String returns the string value at path, or "" if unset. func (c *Config) String(path string) string { if c == nil || c.k == nil { return "" } return c.k.String(path) } func sectionName(filename string) (string, bool) { switch { case strings.HasSuffix(filename, ".yaml"): return strings.TrimSuffix(filename, ".yaml"), true case strings.HasSuffix(filename, ".yml"): return strings.TrimSuffix(filename, ".yml"), true default: return "", false } }