From 049b7044e944633e5567ae41d13e074a6d5b2129 Mon Sep 17 00:00:00 2001 From: Sunggun Yu Date: Fri, 11 Feb 2022 01:43:28 -0500 Subject: [PATCH] feat: refactoring with struct and methods --- cmd/add.go | 35 ++++++++-------- cmd/args.go | 21 +++++----- cmd/cmd_utils.go | 56 ++++++++++++++++++++++++++ cmd/delete.go | 46 +++++++++++---------- cmd/edit.go | 39 ++++++++---------- cmd/list.go | 24 +---------- cmd/root.go | 71 ++++++++------------------------- cmd/show.go | 24 +++-------- cmd/start.go | 43 +++++--------------- cmd/use.go | 56 ++++++++++++++++++++------ go.mod | 1 - internal/config/config.go | 40 +++++++++++++++++++ internal/config/config_test.go | 41 +++++++++++++++++++ internal/config/profile.go | 46 ++++++++++++++++++--- internal/config/profile_test.go | 8 ++++ 15 files changed, 330 insertions(+), 221 deletions(-) create mode 100644 cmd/cmd_utils.go diff --git a/cmd/add.go b/cmd/add.go index 76d42b3..e3474e0 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -48,50 +48,49 @@ func addCommand() *cobra.Command { ), RunE: func(cmd *cobra.Command, args []string) error { - profileName := args[0] + name := args[0] profile := config.Profile{ Desc: flags.desc, Env: []config.Env{}, } profile.Env = config.ParseEnvFlagToEnv(flags.env) - // set profile as default profile if default is empty and no profile is exsiting - if len(configProfiles.AllKeys()) == 0 { - viper.Set(ConfigKeyDefaultProfile, profileName) - } // set profile - configProfiles.Set(profileName, profile) + Config.Profiles.SetProfile(name, profile) - // overwrite the entire profiles - viper.Set(ConfigKeyProfile, configProfiles.AllSettings()) + // set profile as default profile if default is empty and no profile is exsiting + if Config.Default == "" { + Config.Default = name + } // wait for the config file update and verify profile is added or not rc := make(chan error, 1) // it's being watched in root initConfig - viper.WatchConfig() - viper.OnConfigChange(func(e fsnotify.Event) { - // assuming - if configProfiles.Get(profileName) == nil { - rc <- fmt.Errorf("profile %v not added", profileName) + go viper.OnConfigChange(func(e fsnotify.Event) { + if p, _ := Config.Profiles.FindProfile(name); p == nil { + rc <- fmt.Errorf("profile %v not added", name) return } - fmt.Println("profile", profileName, "added successfully:", e.Name) + fmt.Println("Profile", name, "added successfully:", e.Name) rc <- nil }) - if err := viper.WriteConfig(); err != nil { + // update config and save + if err := updateAndSaveConfigFile(&Config, viper.GetViper()); err != nil { return err } + // wait for profile validation channel - err := <-rc - if err != nil { - return err + errOnChange := <-rc + if errOnChange != nil { + return errOnChange } return nil }, } cmd.Flags().StringVarP(&flags.desc, "desc", "d", "", "description of profile") - cmd.Flags().StringSliceVarP(&flags.env, "env", "e", []string{}, "'VAR=VAL' format of string") + cmd.Flags().StringArrayVarP(&flags.env, "env", "e", []string{}, "'VAR=VAL' format of string") cmd.MarkFlagRequired("env") return cmd } diff --git a/cmd/args.go b/cmd/args.go index a8cfbe9..0fc6cfc 100644 --- a/cmd/args.go +++ b/cmd/args.go @@ -8,9 +8,9 @@ import ( func Arg0NotExistingProfile() cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { - selected := configProfiles.Sub(args[0]) - if selected == nil { - return fmt.Errorf("%v is not existing in the profile list", args[0]) + _, err := Config.Profiles.FindProfile(args[0]) + if err != nil { + return err } return nil } @@ -18,12 +18,12 @@ func Arg0NotExistingProfile() cobra.PositionalArgs { func Arg0ExistingProfile() cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { - profiles := configProfiles - if profiles == nil || len(profiles.AllKeys()) == 0 { - return nil - } - selected := profiles.Sub(args[0]) - if selected != nil { + //TODO: what was this? lol + // profiles := configProfiles + // if profiles == nil || len(profiles.AllKeys()) == 0 { + // return nil + // } + if p, _ := Config.Profiles.FindProfile(args[0]); p != nil { return fmt.Errorf("%v is existing already", args[0]) } return nil @@ -42,9 +42,10 @@ func Arg0AsProfileName() cobra.PositionalArgs { } } +// ValidArgsProfileList is for auto complete var ValidArgsProfileList = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveNoFileComp } - return profileList, cobra.ShellCompDirectiveNoFileComp + return Config.Profiles.ProfileNames(), cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/cmd_utils.go b/cmd/cmd_utils.go new file mode 100644 index 0000000..b3189c2 --- /dev/null +++ b/cmd/cmd_utils.go @@ -0,0 +1,56 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/sunggun-yu/envp/internal/config" +) + +// CurrentProfile is function that returns config.Profile +// it checks args and return default profile if args has no profile name +// otherwise speicified profile will be return +func CurrentProfile(args []string) (name string, profile *config.Profile, isDefault bool, err error) { + switch { + case len(args) > 0: + name = args[0] + profile, err = Config.Profiles.FindProfile(name) + if Config.Default == name { + isDefault = true + } + default: + name = Config.Default + isDefault = true + profile, err = Config.DefaultProfile() + } + return name, profile, isDefault, err +} + +// print command example +func printExample(cmd *cobra.Command) { + fmt.Println("Example:") + fmt.Println(cmd.Example) +} + +// check the error type and print out command help for specific error types +func checkErrorAndPrintCommandExample(cmd *cobra.Command, err error) { + switch err.(type) { + case *config.DefaultProfileNotSetError: + printExample(cmd) + case *config.ProfileNameInputEmptyError: + printExample(cmd) + } +} + +// set current status of Config into viper and save it to config file +func updateAndSaveConfigFile(cfg *config.Config, v *viper.Viper) error { + + v.Set("default", cfg.Default) + v.Set("profiles", cfg.Profiles) + + if err := v.WriteConfig(); err != nil { + return err + } + return nil +} diff --git a/cmd/delete.go b/cmd/delete.go index 88c54a9..23e724e 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" + "github.com/fatih/color" "github.com/fsnotify/fsnotify" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -23,26 +24,26 @@ func cmdExampleDelete() string { // deleteCommand delete/remove environment variable profile and it's envionment variables from the config file func deleteCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "delete profile-name", - Short: "Delete environment variable profile", - Aliases: []string{"del"}, - SilenceUsage: true, - Example: cmdExampleDelete(), - Args: cobra.MatchAll( - Arg0AsProfileName(), - Arg0NotExistingProfile(), - ), + Use: "delete profile-name", + Short: "Delete environment variable profile", + Aliases: []string{"del"}, + SilenceUsage: true, + Example: cmdExampleDelete(), ValidArgsFunction: ValidArgsProfileList, RunE: func(cmd *cobra.Command, args []string) error { - p := args[0] - // use built-in function to delete key(profile) from map (profiles) - delete(viper.Get(ConfigKeyProfile).(map[string]interface{}), p) + name, _, isDefault, err := CurrentProfile(args) + if err != nil { + checkErrorAndPrintCommandExample(cmd, err) + return err + } + // delete profile + Config.Profiles.DeleteProfile(name) // set default="" if default profile is being deleted - if p == viper.GetString(ConfigKeyDefaultProfile) { - viper.Set(ConfigKeyDefaultProfile, "") - fmt.Println("WARN: Deleting default profile. please set default profile once it is deleted") + if isDefault { + Config.Default = "" + color.Yellow("WARN: Deleting default profile '%s'. please set default profile once it is deleted", name) } // wait for the config file update and verify profile is added or not @@ -50,22 +51,23 @@ func deleteCommand() *cobra.Command { // I think underlying of viper.OnConfiChange is goroutine. but just run it as goroutine just in case // it's being watched in root initConfig - viper.WatchConfig() go viper.OnConfigChange(func(e fsnotify.Event) { - if configProfiles.Get(p) != nil { - rc <- fmt.Errorf("profile %v not deleted", p) + if p, _ := Config.Profiles.FindProfile(name); p != nil { + rc <- fmt.Errorf("profile %v not deleted", name) return } - fmt.Println("Profile", p, "deleted successfully:", e.Name) + fmt.Println("Profile", name, "deleted successfully:", e.Name) rc <- nil }) - if err := viper.WriteConfig(); err != nil { + // update config and save + if err := updateAndSaveConfigFile(&Config, viper.GetViper()); err != nil { return err } // wait for profile validation channel - err := <-rc - if err != nil { - return err + errOnChange := <-rc + if errOnChange != nil { + return errOnChange } return nil }, diff --git a/cmd/edit.go b/cmd/edit.go index c607951..2b5ef34 100644 --- a/cmd/edit.go +++ b/cmd/edit.go @@ -44,15 +44,10 @@ func editCommand() *cobra.Command { ValidArgsFunction: ValidArgsProfileList, RunE: func(cmd *cobra.Command, args []string) error { - profileName := args[0] - var profile config.Profile - - // validate selected profile - selected := configProfiles.Sub(profileName) - // unmarshal into Profile - err := selected.Unmarshal(&profile) + name, profile, _, err := CurrentProfile(args) if err != nil { - return fmt.Errorf("profile '%v' malformed configuration %e", profile, err) + checkErrorAndPrintCommandExample(cmd, err) + return err } // update desc if input is not empty @@ -73,40 +68,38 @@ func editCommand() *cobra.Command { profile.Env = config.MapToEnv(menv) } - // set updated profile - configProfiles.Set(profileName, profile) - - // overwrite the profile - viper.Set(ConfigKeyProfile, configProfiles.AllSettings()) + // set updated profile. not necessary + Config.Profiles.SetProfile(name, *profile) // wait for the config file update and verify profile is added or not rc := make(chan error, 1) // it's being watched in root initConfig - viper.WatchConfig() - viper.OnConfigChange(func(e fsnotify.Event) { - // assuming - if configProfiles.Get(profileName) == nil { - rc <- fmt.Errorf("profile %v not added", profileName) + go viper.OnConfigChange(func(e fsnotify.Event) { + if p, _ := Config.Profiles.FindProfile(name); p == nil { + rc <- fmt.Errorf("profile %v not updated", name) return } - fmt.Println("profile", profileName, "updated successfully:", e.Name) + fmt.Println("Profile", name, "updated successfully:", e.Name) rc <- nil }) - if err := viper.WriteConfig(); err != nil { + // update config and save + if err := updateAndSaveConfigFile(&Config, viper.GetViper()); err != nil { return err } + // wait for profile validation channel - err = <-rc - if err != nil { - return err + errOnChange := <-rc + if errOnChange != nil { + return errOnChange } return nil }, } cmd.Flags().StringVarP(&flags.desc, "desc", "d", "", "description of profile") - cmd.Flags().StringSliceVarP(&flags.env, "env", "e", []string{}, "'VAR=VAL' format of string") + cmd.Flags().StringArrayVarP(&flags.env, "env", "e", []string{}, "'VAR=VAL' format of string") cmd.MarkFlagRequired("env") return cmd diff --git a/cmd/list.go b/cmd/list.go index ab03879..cfbc492 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -6,7 +6,6 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/sunggun-yu/envp/internal/config" ) func init() { @@ -34,7 +33,7 @@ func listCommand() *cobra.Command { // current default profile name to compare defaultProfile := viper.GetString(ConfigKeyDefaultProfile) // print profiles. mark default profile with * - for _, p := range profileList { + for _, p := range Config.Profiles.ProfileNames() { if p == defaultProfile { color.Green("* %s", p) } else { @@ -46,24 +45,3 @@ func listCommand() *cobra.Command { } return cmd } - -// list all the profiles in dot "." format. e.g. mygroup.my-subgroup.my-profile -// Do DFS to build viper keys for profiles -func listProfileKeys(key string, profiles config.Profiles, arr *[]string) *[]string { - for k, v := range profiles { - var s string - if key == "" { - s = k - } else { - s = fmt.Sprint(key, ".", k) - } - // only Profile item has env items will be considered as profile - // even group(parent Profile that has children Profiles) will be considered as Profile if it has env items. - if len(v.Env) > 0 { - *arr = append(*arr, s) - } - // recursion - listProfileKeys(s, v.Profiles, arr) - } - return arr -} diff --git a/cmd/root.go b/cmd/root.go index 324928e..548de24 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "path/filepath" - "sort" "github.com/fsnotify/fsnotify" "github.com/spf13/cobra" @@ -19,9 +18,8 @@ const ( ) var ( - configProfiles *viper.Viper - profileList []string - rootCmd = rootCommand() + Config config.Config + rootCmd = rootCommand() ) // Execute execute the root command and sub commands @@ -51,12 +49,6 @@ func cmdExampleRoot() string { // rootCommand sets environment variable and execute command line func rootCommand() *cobra.Command { - // profile name from flag or config section "use" - var profile string - var command []string - - // unmarshalled object from selected profile in the config file - var currentProfile config.Profile cmd := &cobra.Command{ Use: "envp profile-name [flags] -- [command line to execute, e.g. kubectl]", @@ -79,47 +71,31 @@ func rootCommand() *cobra.Command { return nil }, ), - // profile validation will be performed - // global var for profile will be unmarshalled - PreRunE: func(cmd *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, args []string) error { + + var profile *config.Profile + var command []string + var err error + /* envp -- command : ArgsLenAtDash == 0 envp profile -- command : ArgsLenAtDash == 1 */ switch { case cmd.ArgsLenAtDash() == 0: - // this case requires default profile. - if viper.GetString(ConfigKeyDefaultProfile) == "" { - printExample(cmd) - return fmt.Errorf("default profile is not set. please set default profile") - } - - profile = viper.GetString(ConfigKeyDefaultProfile) + profile, err = Config.DefaultProfile() command = args case cmd.ArgsLenAtDash() == 1: - profile = args[0] + profile, err = Config.Profiles.FindProfile(args[0]) command = args[1:] } - - // check if selected profile is existing - if configProfiles.Sub(profile) == nil { - return fmt.Errorf("profile '%v' is not existing", profile) - } - - // validate if selected profile is existing in the config - selected := configProfiles.Sub(profile) - // unmarshal to Profile - err := selected.Unmarshal(¤tProfile) if err != nil { - return fmt.Errorf("profile '%v' malformed configuration %e", profile, err) + checkErrorAndPrintCommandExample(cmd, err) + return err } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - // Execute command - if err := shell.Execute(command, currentProfile.Env); err != nil { + if err := shell.Execute(command, profile.Env); err != nil { return err } return nil @@ -185,24 +161,11 @@ func configPath(base string) string { return path } -// print example only -func printExample(cmd *cobra.Command) { - fmt.Println("Example:") - fmt.Println(cmd.Example) -} - -// TODO: refactoring -// init profiles and profile list +// unmarshal config.Config func initProfiles() { - // reload profiles - configProfiles = viper.Sub(ConfigKeyProfile) - // unmarshal config item "profiles" to Profiles - var root config.Profiles - err := configProfiles.Unmarshal(&root) + err := viper.Unmarshal(&Config) if err != nil { - return + fmt.Println(err) + os.Exit(1) } - // generate profile list - profileList = *listProfileKeys("", root, &[]string{}) - sort.Strings(profileList) } diff --git a/cmd/show.go b/cmd/show.go index ba5503b..1e47b2e 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -4,8 +4,6 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/sunggun-yu/envp/internal/config" ) // flags struct for show command @@ -29,8 +27,8 @@ func cmdExampleShow() string { // showCommand prints out all the environment variables of profile func showCommand() *cobra.Command { + var flags showFlags - var profileName string cmd := &cobra.Command{ Use: "show profile-name [flags]", @@ -40,25 +38,13 @@ func showCommand() *cobra.Command { Example: cmdExampleShow(), RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - profileName = viper.GetString(ConfigKeyDefaultProfile) - } else { - profileName = args[0] - } - - var profile config.Profile - // validate selected profile - selected := configProfiles.Sub(profileName) - if selected == nil { - return fmt.Errorf("profile %v is not existing", profileName) - } - - // unmarshal into Profile - err := selected.Unmarshal(&profile) + name, profile, _, err := CurrentProfile(args) if err != nil { - return fmt.Errorf("profile '%v' malformed configuration %e", profile, err) + checkErrorAndPrintCommandExample(cmd, err) + return err } + fmt.Println("# profile:", name) if flags.export { fmt.Println("# you can export env vars of profile with following command") fmt.Println("# eval $(envp show --export)") diff --git a/cmd/start.go b/cmd/start.go index 2a46142..8463570 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -6,8 +6,6 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/sunggun-yu/envp/internal/config" "github.com/sunggun-yu/envp/internal/shell" ) @@ -30,9 +28,6 @@ func cmdExampleStart() string { // deleteCommand delete/remove environment variable profile and it's envionment variables from the config file func startCommand() *cobra.Command { - var profile string - // unmarshalled object from selected profile in the config file - var currentProfile config.Profile cmd := &cobra.Command{ Use: "start profile-name", @@ -40,45 +35,27 @@ func startCommand() *cobra.Command { SilenceUsage: true, Example: cmdExampleStart(), ValidArgsFunction: ValidArgsProfileList, - PreRunE: func(cmd *cobra.Command, args []string) error { - // TODO: refactoring, cleanup - switch { - case len(args) == 0: - // this case requires default profile. - if viper.GetString(ConfigKeyDefaultProfile) == "" { - printExample(cmd) - return fmt.Errorf("default profile is not set. please set default profile") - } - profile = viper.GetString(ConfigKeyDefaultProfile) - case len(args) > 0: - profile = args[0] - } - // validate if selected profile is existing in the config - selected := configProfiles.Sub(profile) - // unmarshal to Profile - err := selected.Unmarshal(¤tProfile) - if err != nil { - return fmt.Errorf("profile '%v' malformed configuration %e", profile, err) - } - return nil - }, RunE: func(cmd *cobra.Command, args []string) error { - // TODO: refactoring, cleanup + name, profile, _, err := CurrentProfile(args) + if err != nil { + checkErrorAndPrintCommandExample(cmd, err) + return err + } // print start of session message - fmt.Println(color.GreenString("Starting ENVP session..."), color.RedString(profile)) - color.Cyan(currentProfile.Env.String()) + fmt.Println(color.GreenString("Starting ENVP session..."), color.RedString(name)) + color.Cyan(profile.Env.String()) fmt.Println("> press ctrl+d or type 'exit' to close session") // set ENVP_PROFILE env var to leverage profile info in the prompt, such as starship. - os.Setenv(envpEnvVarKey, profile) + os.Setenv(envpEnvVarKey, name) // ignore error message from shell. let shell print out the errors - shell.StartShell(currentProfile.Env) + shell.StartShell(profile.Env) // print end of session message - fmt.Println(color.GreenString("ENVP session closed..."), color.RedString(profile)) + fmt.Println(color.GreenString("ENVP session closed..."), color.RedString(name)) return nil }, } diff --git a/cmd/use.go b/cmd/use.go index 38bdb21..0bdef12 100644 --- a/cmd/use.go +++ b/cmd/use.go @@ -2,7 +2,10 @@ package cmd import ( "fmt" + "os" + "github.com/fatih/color" + "github.com/fsnotify/fsnotify" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -25,22 +28,49 @@ func cmdExampleUse() string { // add command func useCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "use profile-name", - Short: "Set default environment variable profile", - SilenceUsage: true, - Example: cmdExampleUse(), - Args: cobra.MatchAll( - Arg0AsProfileName(), - Arg0NotExistingProfile(), - ), + Use: "use profile-name", + Short: "Set default environment variable profile", + SilenceUsage: true, + Example: cmdExampleUse(), ValidArgsFunction: ValidArgsProfileList, RunE: func(cmd *cobra.Command, args []string) error { - p := args[0] - viper.Set(ConfigKeyDefaultProfile, p) - if err := viper.WriteConfig(); err != nil { - return fmt.Errorf("failed to updating the config file: %v", err.Error()) + name, _, isDefault, err := CurrentProfile(args) + if err != nil { + checkErrorAndPrintCommandExample(cmd, err) + return err + } + // just exit if selected profile is already default + if isDefault { + fmt.Println("Profile", name, "is alreday set as default") + os.Exit(0) + } + + // set selected profile as default + Config.Default = name + + // wait for the config file update and verify profile is added or not + rc := make(chan error, 1) + + // it's being watched in root initConfig - viper.WatchConfig() + go viper.OnConfigChange(func(e fsnotify.Event) { + if Config.Default != name { + rc <- fmt.Errorf("default profile is not updated") + return + } + fmt.Println("Default profile is set to", color.GreenString(Config.Default)) + rc <- nil + }) + + // update config and save + if err := updateAndSaveConfigFile(&Config, viper.GetViper()); err != nil { + return err + } + + // wait for profile validation channel + errOnChange := <-rc + if errOnChange != nil { + return errOnChange } - fmt.Println("Default profile is set to", viper.GetString(ConfigKeyDefaultProfile)) return nil }, } diff --git a/go.mod b/go.mod index 464eb94..c354397 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.16 require ( github.com/fatih/color v1.13.0 github.com/fsnotify/fsnotify v1.5.1 - github.com/mitchellh/mapstructure v1.4.3 github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.10.1 gopkg.in/yaml.v2 v2.4.0 diff --git a/internal/config/config.go b/internal/config/config.go index 493afd5..b16cb5e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,6 +11,46 @@ type Config struct { Profiles *Profiles `mapstructure:"profiles" yaml:"profiles"` } +type DefaultProfileNotSetError struct{} + +// NewProfileNotExistingError create new error +func NewDefaultProfileNotSetError() *DefaultProfileNotSetError { + return &DefaultProfileNotSetError{} +} + +// Error is to make ProfileNotExistingError errors +func (e *DefaultProfileNotSetError) Error() string { + return "default profile is not set" +} + +func (c *Config) DefaultProfile() (*Profile, error) { + if c.Default == "" { + return nil, NewDefaultProfileNotSetError() + } + return c.Profiles.FindProfile(c.Default) +} + +// Profile find and return Profile from name +// if name is empty it returns default Profile from Profiles +// otherwise speicified Profile will be return +// error will be return when default is not set or profile is not existing +func (c *Config) Profile(name string) (*Profile, error) { + + var profile *Profile + var err error + + switch { + case name != "": + profile, err = c.Profiles.FindProfile(name) + default: + profile, err = c.DefaultProfile() + } + if err != nil { + return nil, err + } + return profile, nil +} + // ParseEnvFlagToMap parse string format "env=val" to map "env: val". it can be used fo dup check from slice of Env func ParseEnvFlagToMap(envs []string) map[string]string { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index aaf7be0..be6289c 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -1,6 +1,7 @@ package config_test import ( + "fmt" "io/ioutil" "reflect" "testing" @@ -113,3 +114,43 @@ func TestMapToEnv(t *testing.T) { t.Error("Not meet expectation", expected, "-", actual) } } + +func TestDefaultProfile(t *testing.T) { + cfg := testDataConfig() + if p, err := cfg.DefaultProfile(); err != nil { + t.Error("Should not be nil") + } else { + fmt.Println(p) + } + // make default empty + cfg.Default = "" + if _, err := cfg.DefaultProfile(); err == nil { + t.Error("Should be error") + } else { + fmt.Println(err) + } +} + +func TestProfile(t *testing.T) { + cfg := testDataConfig() + + if p, err := cfg.Profile(""); err != nil { + t.Error("Should not be nil.") + } else if p.Desc != "docker" { + t.Error("Should not be same as default profile") + } + + if p, err := cfg.Profile("docker"); err != nil { + t.Error("Should not be nil.") + } else if p == nil { + t.Error("Should not be nil.") + } + + // make default empty and find profile that is not existing + cfg.Default = "" + if _, err := cfg.Profile("not-existing-profile"); err == nil { + t.Error("Should be error") + } else { + fmt.Println(err) + } +} diff --git a/internal/config/profile.go b/internal/config/profile.go index bd0ac8d..31b3093 100644 --- a/internal/config/profile.go +++ b/internal/config/profile.go @@ -15,8 +15,8 @@ type Profile struct { // set it with mapstructure remain to unmashal config file item `profiles` as Profile // yaml inline fixed the nested profiles issue Profiles Profiles `mapstructure:",remain" yaml:",inline"` - Desc string `mapstructure:"desc" yaml:"desc"` - Env Envs `mapstructure:"env" yaml:"env"` + Desc string `mapstructure:"desc" yaml:"desc,omitempty"` + Env Envs `mapstructure:"env" yaml:"env,omitempty"` } func NewProfile() *Profile { @@ -35,6 +35,36 @@ type Env struct { Value string `mapstructure:"value" yaml:"value"` } +// ProfileNotExistingError is an error when expected profile is not existing +type ProfileNotExistingError struct { + profile string +} + +// NewProfileNotExistingError create new ProfileNotExistingError +func NewProfileNotExistingError(profile string) *ProfileNotExistingError { + return &ProfileNotExistingError{ + profile: profile, + } +} + +// Error is to make ProfileNotExistingError errors +func (e *ProfileNotExistingError) Error() string { + return fmt.Sprintf("profile %s is not exising", e.profile) +} + +// ProfileNameInputEmptyError is an error when mandatory profile input is empty +type ProfileNameInputEmptyError struct{} + +// Error is to make ProfileNotExistingError errors +func (e *ProfileNameInputEmptyError) Error() string { + return "input profile name is empty" +} + +// NewProfileNameInputEmptyError create new ProfileNameInputEmptyError +func NewProfileNameInputEmptyError() *ProfileNameInputEmptyError { + return &ProfileNameInputEmptyError{} +} + // Override String() to make it KEY=VAL format func (e Env) String() string { return fmt.Sprint(e.Name, "=", e.Value) @@ -55,7 +85,7 @@ func (e Envs) String() string { // if it is dot delimeterd, considering it as nested profile func (p *Profiles) SetProfile(key string, profile Profile) error { if key == "" { - return fmt.Errorf("empty profile name") + return NewProfileNameInputEmptyError() } keys := strings.Split(key, ".") if len(keys) == 1 { @@ -90,10 +120,13 @@ func (p *Profiles) SetProfile(key string, profile Profile) error { // FindProfile finds profile from dot notation of profile name such as "a.b.c" func (p *Profiles) FindProfile(key string) (*Profile, error) { + if key == "" { + return nil, NewProfileNameInputEmptyError() + } keys := strings.Split(key, ".") result := findProfileByDotNotationKey(keys, p) if result == nil { - return nil, fmt.Errorf("profile %v is not exising", key) + return nil, NewProfileNotExistingError(key) } return result, nil } @@ -122,6 +155,9 @@ func (p *Profiles) ProfileNames() []string { // DeleteProfile delete profile func (p *Profiles) DeleteProfile(key string) error { + if key == "" { + return NewProfileNameInputEmptyError() + } keys := strings.Split(key, ".") @@ -139,7 +175,7 @@ func (p *Profiles) DeleteProfile(key string) error { pp, err := p.FindParentProfile(key) if err != nil { // no parent means profile is not existing - return fmt.Errorf("profile %v is not existing", key) + return NewProfileNotExistingError(key) } parent = pp.Profiles } diff --git a/internal/config/profile_test.go b/internal/config/profile_test.go index 26a22fe..e41849d 100644 --- a/internal/config/profile_test.go +++ b/internal/config/profile_test.go @@ -137,6 +137,14 @@ func TestDeleteProfile(t *testing.T) { t.Error("Parent should not be nil after deleting") } } + + // error case + err := profile.DeleteProfile("") + if err == nil { + t.Error("deleting empty string profile name should be error") + } else if err.Error() == "" { + t.Error("should return some error message") + } } var testCaseNonExistingProfile = func(key string) {