diff --git a/cmd/mop/main.go b/cmd/mop/main.go index 2418a52..0e7e8e8 100644 --- a/cmd/mop/main.go +++ b/cmd/mop/main.go @@ -6,10 +6,14 @@ package main import ( "flag" + "fmt" + "os" "os/user" "path" + "strings" "time" + "github.com/eiannone/keyboard" "github.com/mop-tracker/mop" "github.com/nsf/termbox-go" ) @@ -138,7 +142,29 @@ func main() { profileName := flag.String("profile", path.Join(usr.HomeDir, defaultProfile), "path to profile") flag.Parse() - profile := mop.NewProfile(*profileName) + profile, err := mop.NewProfile(*profileName) + if err != nil { + fmt.Fprintf(os.Stderr, "The profile read from `%s` is corrupted.\n\tError: %s\n\n", *profileName, err) + + // Loop until we get a "y" or "n" answer. + // Note: This is only for the interactive mode. Once we have the "one-shot", this should be skipped + for { + fmt.Fprintln(os.Stderr, "Do you want to overwrite the current profile with the default one? [y/n]") + rne, _, _ := keyboard.GetSingleKey() + res := strings.ToLower(string(rne)) + if res != "y" && res != "n" { + fmt.Fprintf(os.Stderr, "Invalid answer `%s`\n\n", res) + continue + } + + if res == "y" { + profile.InitDefaultProfile() + break + } else { + os.Exit(1) + } + } + } screen := mop.NewScreen(profile) defer screen.Close() diff --git a/go.mod b/go.mod index b08be4e..8fb4af4 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.15 require ( github.com/Knetic/govaluate v3.0.0+incompatible + github.com/eiannone/keyboard v0.0.0-20200508000154-caf4b762e807 github.com/mattn/go-runewidth v0.0.13 // indirect github.com/nsf/termbox-go v1.1.1 + golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect ) diff --git a/go.sum b/go.sum index ffcb180..13a636e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/eiannone/keyboard v0.0.0-20200508000154-caf4b762e807 h1:jdjd5e68T4R/j4PWxfZqcKY8KtT9oo8IPNVuV4bSXDQ= +github.com/eiannone/keyboard v0.0.0-20200508000154-caf4b762e807/go.mod h1:Xoiu5VdKMvbRgHuY7+z64lhu/7lvax/22nzASF6GrO8= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -10,3 +12,5 @@ github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/profile.go b/profile.go index d6a3779..93bbfaa 100644 --- a/profile.go +++ b/profile.go @@ -44,6 +44,7 @@ type Profile struct { filename string // Path to the file in which the configuration is stored } +// Checks if a string represents a supported color or not. func IsSupportedColor(colorName string) bool { switch colorName { case @@ -70,41 +71,51 @@ func IsSupportedColor(colorName string) bool { // Creates the profile and attempts to load the settings from ~/.moprc file. // If the file is not there it gets created with default values. -func NewProfile(filename string) *Profile { +func NewProfile(filename string) (*Profile, error) { profile := &Profile{filename: filename} data, err := ioutil.ReadFile(filename) - if err != nil { // Set default values: - profile.MarketRefresh = 12 // Market data gets fetched every 12s (5 times per minute). - profile.QuotesRefresh = 5 // Stock quotes get updated every 5s (12 times per minute). - profile.Grouped = false // Stock quotes are *not* grouped by advancing/declining. - profile.Tickers = []string{`AAPL`, `C`, `GOOG`, `IBM`, `KO`, `ORCL`, `V`} - profile.SortColumn = 0 // Stock quotes are sorted by ticker name. - profile.Ascending = true // A to Z. - profile.Filter = "" - profile.Colors.Gain = defaultGainColor - profile.Colors.Loss = defaultLossColor - profile.Colors.Tag = defaultTagColor - profile.Colors.Header = defaultHeaderColor - profile.Colors.Time = defaultTimeColor - profile.Colors.Default = defaultColor - profile.Save() + if err == nil { + err = json.Unmarshal(data, profile) + + if err == nil { + InitColor(&profile.Colors.Gain, defaultGainColor) + InitColor(&profile.Colors.Loss, defaultLossColor) + InitColor(&profile.Colors.Tag, defaultTagColor) + InitColor(&profile.Colors.Header, defaultHeaderColor) + InitColor(&profile.Colors.Time, defaultTimeColor) + InitColor(&profile.Colors.Default, defaultColor) + + profile.SetFilter(profile.Filter) + } } else { - json.Unmarshal(data, profile) - - InitColor(&profile.Colors.Gain, defaultGainColor) - InitColor(&profile.Colors.Loss, defaultLossColor) - InitColor(&profile.Colors.Tag, defaultTagColor) - InitColor(&profile.Colors.Header, defaultHeaderColor) - InitColor(&profile.Colors.Time, defaultTimeColor) - InitColor(&profile.Colors.Default, defaultColor) - - profile.SetFilter(profile.Filter) + profile.InitDefaultProfile() + err = nil } profile.selectedColumn = -1 - return profile + return profile, err +} + +// Initializes a profile with the default values +func (profile *Profile) InitDefaultProfile() { + profile.MarketRefresh = 12 // Market data gets fetched every 12s (5 times per minute). + profile.QuotesRefresh = 5 // Stock quotes get updated every 5s (12 times per minute). + profile.Grouped = false // Stock quotes are *not* grouped by advancing/declining. + profile.Tickers = []string{`AAPL`, `C`, `GOOG`, `IBM`, `KO`, `ORCL`, `V`} + profile.SortColumn = 0 // Stock quotes are sorted by ticker name. + profile.Ascending = true // A to Z. + profile.Filter = "" + profile.Colors.Gain = defaultGainColor + profile.Colors.Loss = defaultLossColor + profile.Colors.Tag = defaultTagColor + profile.Colors.Header = defaultHeaderColor + profile.Colors.Time = defaultTimeColor + profile.Colors.Default = defaultColor + profile.Save() } +// Initializes a color to the given string, or to the default value if the given +// string does not represent a supported color. func InitColor(color *string, defaultValue string) { *color = strings.ToLower(*color) if !IsSupportedColor(*color) {