aboutsummaryrefslogtreecommitdiff
path: root/config
diff options
context:
space:
mode:
Diffstat (limited to 'config')
-rw-r--r--config/config.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..3c93d53
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,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(path string) (MainConfig, error) {
+ pathFromEnv := os.Getenv("EXAMPLE_CONFIG")
+ if pathFromEnv != "" {
+ path = pathFromEnv
+ }
+
+ 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(path string) (MainConfig, error) {
+ addLoggerOptions()
+ addSomethingOptions()
+
+ pflag.Parse()
+ viper.BindPFlags(pflag.CommandLine) //nolint:errcheck
+
+ return loadConfigFromFile(path)
+}