11package cmd
22
33import (
4+ "errors"
45 "fmt"
6+ "strings"
57
68 "github.com/karust/openserp/core"
79 "github.com/sirupsen/logrus"
@@ -11,60 +13,66 @@ import (
1113)
1214
1315const (
14- version = "0.2.1"
15- defaultConfigFilename = "config"
16- envPrefix = "OPENSERP"
17- replaceHyphenWithCamelCase = false
16+ version = "0.2.1"
17+ defaultConfigFilename = "config"
18+ envPrefix = "OPENSERP"
1819)
1920
21+ type Config struct {
22+ App AppConfig `mapstructure:"app"`
23+ GoogleConfig core.SearchEngineOptions `mapstructure:"google"`
24+ YandexConfig core.SearchEngineOptions `mapstructure:"yandex"`
25+ BaiduConfig core.SearchEngineOptions `mapstructure:"baidu"`
26+ }
27+
2028type AppConfig struct {
21- Host string
22- Port int
23- Timeout int
24- ConfigPath string
25- IsBrowserHead bool `mapstructure:"head"`
26- IsLeaveHead bool `mapstructure:"leave_head"`
27- IsLeakless bool `mapstructure:"leakless"`
28- IsDebug bool `mapstructure:"debug"`
29- IsVerbose bool `mapstructure:"verbose"`
30- IsRawRequests bool `mapstructure:"raw_requests"`
31- GoogleConfig core.SearchEngineOptions `mapstructure:"google"`
32- YandexConfig core.SearchEngineOptions `mapstructure:"yandex"`
33- BaiduConfig core.SearchEngineOptions `mapstructure:"baidu"`
29+ Host string `mapstructure:"host"`
30+ Port int `mapstructure:"port"`
31+ Timeout int `mapstructure:"timeout"`
32+ ConfigPath string `mapstructure:"config_path"`
33+ IsBrowserHead bool `mapstructure:"head"`
34+ IsLeaveHead bool `mapstructure:"leave_head"`
35+ IsLeakless bool `mapstructure:"leakless"`
36+ IsDebug bool `mapstructure:"debug"`
37+ IsVerbose bool `mapstructure:"verbose"`
38+ IsRawRequests bool `mapstructure:"raw_requests"`
3439}
3540
36- var appConf = AppConfig {}
41+ var config = Config {}
3742
3843var RootCmd = & cobra.Command {
39- Use : "openserp" ,
40- Short : "Open SERP" ,
41- Long : `Search via Google, Yandex and Baidu` ,
42- Version : version ,
44+ Use : "openserp" ,
45+ Short : "Open SERP" ,
46+ Long : `Get [Google, Yandex, Baidu] search engine results via API or CLI.` ,
47+ Version : version ,
48+ SilenceUsage : true ,
4349 PersistentPreRunE : func (cmd * cobra.Command , args []string ) error {
44- core .InitLogger (appConf .IsVerbose , appConf .IsDebug )
50+ core .InitLogger (config .App .IsVerbose , config .App .IsDebug )
51+
4552 err := initializeConfig (cmd )
46- logrus .Debugf ("Config: %+v" , appConf )
47- return err
53+ if err != nil {
54+ return err
55+ }
56+
57+ logrus .Debugf ("Final config: %+v" , config )
58+ return nil
4859 },
60+
4961 // Run: func(cmd *cobra.Command, args []string) {
5062 // // Working with OutOrStdout/OutOrStderr allows us to unit test our command easier
5163 // //out := cmd.OutOrStdout()
52- // logrus.Trace("Config:", appConf )
64+ // logrus.Trace("Config:", config )
5365 // },
5466}
5567
5668// Bind each cobra flag to its associated viper configuration (config file and environment variable)
5769func bindFlags (cmd * cobra.Command , vpr * viper.Viper ) {
5870 cmd .Flags ().VisitAll (func (flg * pflag.Flag ) {
5971 configName := "app." + flg .Name
60- //if replaceHyphenWithCamelCase {
61- // configName = strings.ReplaceAll(f.Name, "-", "")
62- //}
6372
6473 // Apply viper config value to the flag if viper has a value
65- if ! flg .Changed && vpr .IsSet (configName ) {
66- val := vpr .Get (configName )
67- cmd .Flags ().Set (flg .Name , fmt .Sprintf ("%v" , val ))
74+ if flg .Changed {
75+ vpr .Set (configName , flg .Value )
6876 }
6977 })
7078}
@@ -77,34 +85,47 @@ func initializeConfig(cmd *cobra.Command) error {
7785 v .SetConfigName (defaultConfigFilename )
7886 v .AddConfigPath ("." )
7987
80- // Return an error if we cannot parse the config file.
81- if err := v .ReadInConfig (); err != nil {
82- if _ , ok := err .(viper. ConfigFileNotFoundError ); ! ok {
83- return err
84- }
88+ // 1. Config. Return an error if we cannot parse the config file.
89+ err := v .ReadInConfig ()
90+ if err != nil {
91+ err = errors . New ( fmt . Sprintf ( "Cannot read config: %v" , err ))
92+ logrus . Warn ( err )
8593 }
8694
87- v .SetEnvPrefix (envPrefix )
88-
89- // Bind environment variables to their equivalent keys with underscores
90- //v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
91- v .AutomaticEnv ()
95+ // 2. Env. Bind environment variables to their equivalent keys with underscores
96+ for _ , key := range v .AllKeys () {
97+ envKey := envPrefix + "_" + strings .ToUpper (strings .ReplaceAll (key , "." , "_" ))
98+ err := v .BindEnv (key , envKey )
99+ if err != nil {
100+ logrus .Errorf ("Unable to bind ENV valye: %v" , err )
101+ }
102+ }
92103
93- // Bind the current command's flags to viper
104+ // 3. Cmd flags. Bind the current command's flags to viper
94105 bindFlags (cmd , v )
95106
107+ // Dump Viper values to config struct
108+ err = v .Unmarshal (& config )
109+ if err != nil {
110+ return errors .New (fmt .Sprintf ("Cannot unmarshall config: %v" , err ))
111+ }
112+
113+ if config .App .IsDebug {
114+ logrus .Debug ("Viper config:" )
115+ v .Debug ()
116+ }
96117 return nil
97118}
98119
99120func init () {
100- RootCmd .PersistentFlags ().IntVarP (& appConf .Port , "port" , "p" , 7070 , "Port number to run server" )
101- RootCmd .PersistentFlags ().StringVarP (& appConf .Host , "host" , "a" , "127.0.0.1" , "Host address to run server" )
102- RootCmd .PersistentFlags ().IntVarP (& appConf .Timeout , "timeout" , "t" , 30 , "Timeout to fail request" )
103- RootCmd .PersistentFlags ().StringVarP (& appConf . ConfigPath , "config" , "c" , "./config.yaml " , "Configuration file path" )
104- RootCmd .PersistentFlags ().BoolVarP (& appConf .IsVerbose , "verbose" , "v" , false , "Use verbose output" )
105- RootCmd .PersistentFlags ().BoolVarP (& appConf .IsDebug , "debug" , "d" , false , "Use debug output. Disable headless browser" )
106- RootCmd .PersistentFlags ().BoolVarP (& appConf .IsBrowserHead , "head" , "" , false , "Enable browser UI" )
107- RootCmd .PersistentFlags ().BoolVarP (& appConf .IsLeakless , "leakless" , "l" , false , "Use leakless mode to insure browser instances are closed after search" )
108- RootCmd .PersistentFlags ().BoolVarP (& appConf .IsRawRequests , "raw" , "r" , false , "Disable browser usage, use HTTP requests" )
109- RootCmd .PersistentFlags ().BoolVarP (& appConf .IsLeaveHead , "leave" , "" , false , "Leave browser and tabs opened after search is made" )
121+ RootCmd .PersistentFlags ().IntVarP (& config . App .Port , "port" , "p" , 7070 , "Port number to run server" )
122+ RootCmd .PersistentFlags ().StringVarP (& config . App .Host , "host" , "a" , "127.0.0.1" , "Host address to run server" )
123+ RootCmd .PersistentFlags ().IntVarP (& config . App .Timeout , "timeout" , "t" , 30 , "Timeout to fail request" )
124+ RootCmd .PersistentFlags ().StringVarP (& config . App . ConfigPath , "config" , "c" , "" , "Configuration file path" )
125+ RootCmd .PersistentFlags ().BoolVarP (& config . App .IsVerbose , "verbose" , "v" , false , "Use verbose output" )
126+ RootCmd .PersistentFlags ().BoolVarP (& config . App .IsDebug , "debug" , "d" , false , "Use debug output. Disable headless browser" )
127+ RootCmd .PersistentFlags ().BoolVarP (& config . App .IsBrowserHead , "head" , "" , false , "Enable browser UI" )
128+ RootCmd .PersistentFlags ().BoolVarP (& config . App .IsLeakless , "leakless" , "l" , false , "Use leakless mode to insure browser instances are closed after search" )
129+ RootCmd .PersistentFlags ().BoolVarP (& config . App .IsRawRequests , "raw" , "r" , false , "Disable browser usage, use HTTP requests" )
130+ RootCmd .PersistentFlags ().BoolVarP (& config . App .IsLeaveHead , "leave" , "" , false , "Leave browser and tabs opened after search is made" )
110131}
0 commit comments