1package config
2
3import (
4 "os"
5 "path/filepath"
6 "sort"
7 "strings"
8)
9
10// DefaultConfigDirs lists the base directories searched for config files,
11// in ascending priority order.
12var DefaultConfigDirs = []string{
13 "/usr/share/keld",
14 "/etc/keld",
15 "~/.config/keld",
16}
17
18// DiscoverFiles returns config file paths in ascending priority order.
19//
20// The search order is:
21// 1. For each directory in DefaultConfigDirs: config.toml, then sorted conf.d/*.toml
22// 2. Paths/globs from the KELD_CONFIG_PATHS env var (colon-separated)
23// 3. If KELD_CONFIG_FILE is set, it replaces ALL of the above
24//
25// All paths support ~ (home dir) and $VAR expansion.
26func DiscoverFiles() []string {
27 return discoverFiles(os.Getenv)
28}
29
30// discoverFiles is the testable core; getenv abstracts os.Getenv.
31func discoverFiles(getenv func(string) string) []string {
32 if single := getenv("KELD_CONFIG_FILE"); single != "" {
33 return []string{ExpandPath(single)}
34 }
35
36 var paths []string
37
38 for _, dir := range DefaultConfigDirs {
39 dir = ExpandPath(dir)
40
41 paths = append(paths, filepath.Join(dir, "config.toml"))
42 paths = append(paths, sortedGlob(filepath.Join(dir, "conf.d", "*.toml"))...)
43 }
44
45 if extra := getenv("KELD_CONFIG_PATHS"); extra != "" {
46 for _, entry := range strings.Split(extra, ":") {
47 entry = ExpandPath(strings.TrimSpace(entry))
48 if strings.ContainsAny(entry, "*?[") {
49 paths = append(paths, sortedGlob(entry)...)
50 } else {
51 paths = append(paths, entry)
52 }
53 }
54 }
55
56 return paths
57}
58
59// ExpandPath expands ~ to the user's home directory and $VAR references.
60func ExpandPath(p string) string {
61 if strings.HasPrefix(p, "~/") || p == "~" {
62 home, err := os.UserHomeDir()
63 if err == nil {
64 p = home + p[1:]
65 }
66 }
67 return os.ExpandEnv(p)
68}
69
70// sortedGlob returns glob matches in sorted order, or nil on error.
71func sortedGlob(pattern string) []string {
72 matches, err := filepath.Glob(pattern)
73 if err != nil || len(matches) == 0 {
74 return nil
75 }
76 sort.Strings(matches)
77 return matches
78}