Skip to content

Commit

Permalink
feat: refactoring with struct and methods
Browse files Browse the repository at this point in the history
  • Loading branch information
sunggun-yu committed Feb 11, 2022
1 parent 0e7f2ea commit 049b704
Show file tree
Hide file tree
Showing 15 changed files with 330 additions and 221 deletions.
35 changes: 17 additions & 18 deletions cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
21 changes: 11 additions & 10 deletions cmd/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ 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
}
}

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
Expand All @@ -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
}
56 changes: 56 additions & 0 deletions cmd/cmd_utils.go
Original file line number Diff line number Diff line change
@@ -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
}
46 changes: 24 additions & 22 deletions cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"fmt"

"github.com/fatih/color"
"github.com/fsnotify/fsnotify"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -23,49 +24,50 @@ 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
rc := make(chan error, 1)
// 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
},
Expand Down
39 changes: 16 additions & 23 deletions cmd/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
24 changes: 1 addition & 23 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Loading

0 comments on commit 049b704

Please sign in to comment.