aboutsummaryrefslogtreecommitdiff
path: root/config/config.go
blob: c28fa14ae013decd5e7941a0958dd94869227675 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package config
 
import (
"errors"
"fmt"
"os"
"path"
"strings"
 
"github.com/mitchellh/mapstructure"
"github.com/rs/zerolog"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
 
type LoggerConfig struct {
Verbose bool   `mapstructure:"verbose"`
Format  string `mapstructure:"format"`
}
 
func (c *LoggerConfig) MarshalZerologObject(e *zerolog.Event) {
e.
Bool("verbose", c.Verbose).
Str("format", c.Format)
}
 
type SomethingConfig struct {
Value int `mapstructure:"value"`
}
 
func (c *SomethingConfig) MarshalZerologObject(event *zerolog.Event) {
event.
Int("value", c.Value)
}
 
type MainConfig struct {
Something SomethingConfig `mapstructure:"something"`
Logger    LoggerConfig    `mapstructure:"logger"`
 
configFile string
}
 
func (c *MainConfig) MarshalZerologObject(event *zerolog.Event) {
event.
Str("config-file", c.configFile).
Object("logger", &c.Logger).
Object("something", &c.Something)
}
 
func addLoggerOptions() {
pflag.Bool("verbose", false, "log at debug level")
viper.BindPFlag("logger.verbose", pflag.Lookup("verbose")) //nolint:errcheck
pflag.String("log-format", "json", "logging format (json, console)")
viper.BindPFlag("logger.format", pflag.Lookup("log-format")) //nolint:errcheck
}
 
func addSomethingOptions() {
pflag.Int("something-value", 123, "value used to do something")
viper.BindPFlag("something.value", pflag.Lookup("something-value")) //nolint:errcheck
viper.BindEnv("something.value", "SOMETHING_VALUE")                 //nolint:errcheck
}
 
func addConfigFromFile(configRelPath string) error {
mode := os.Getenv("RUN_MODE")
if mode == "" {
mode = "development"
}
 
viper.SetConfigName("example." + mode)
 
myName, err := os.Executable()
if err == nil && !strings.HasPrefix(configRelPath, "/") {
myDir := path.Dir(myName)
viper.AddConfigPath(path.Join(myDir, configRelPath))
} else {
viper.AddConfigPath(configRelPath)
}
 
err = viper.ReadInConfig()
if err != nil {
var notFound viper.ConfigFileNotFoundError
if !errors.As(err, &notFound) {
return fmt.Errorf("failed to read config file at %s: %w", viper.ConfigFileUsed(), err)
}
}
 
return nil
}
 
func loadConfigFromFile() (MainConfig, error) {
path := os.Getenv("EXAMPLE_CONFIG")
if path == "" {
path = "."
}
 
err := addConfigFromFile(path)
if err != nil {
return MainConfig{}, err
}
 
var config MainConfig
 
err = viper.Unmarshal(&config, viper.DecodeHook(
mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToIPHookFunc(),
),
))
if err != nil {
return MainConfig{}, fmt.Errorf("failed to parse options: %w", err)
}
 
config.configFile = viper.ConfigFileUsed()
 
return config, nil
}
 
func GetMainConfig() (MainConfig, error) {
addLoggerOptions()
addSomethingOptions()
 
pflag.Parse()
viper.BindPFlags(pflag.CommandLine) //nolint:errcheck
 
return loadConfigFromFile()
}