diff --git a/internal/validate/validate.go b/internal/validate/validate.go index 0c13693..420ecdb 100644 --- a/internal/validate/validate.go +++ b/internal/validate/validate.go @@ -7,6 +7,7 @@ import ( ) type Config struct { + Now bool WakeTime string SleepTime string FromTime string @@ -17,14 +18,14 @@ type Config struct { } func (c *Config) Validate() error { - if err := validateModes(c.WakeTime, c.SleepTime, c.FromTime, c.ToTime); err != nil { + if err := validateModes(c.Now, c.WakeTime, c.SleepTime, c.FromTime, c.ToTime); err != nil { return err } if c.FromTime != "" || c.ToTime != "" { if err := validateWindow(c.FromTime, c.ToTime); err != nil { return err } - } else { + } else if c.WakeTime != "" || c.SleepTime != "" { if err := validateTimeFlags(c.WakeTime, c.SleepTime); err != nil { return err } @@ -35,11 +36,22 @@ func (c *Config) Validate() error { return validateCycles(c.MinCycles, c.MaxCycles) } -func validateModes(wake, sleep, from, to string) error { - windowSet := from != "" || to != "" - wakeSleepSet := wake != "" || sleep != "" - if windowSet && wakeSleepSet { - return fmt.Errorf("cannot use --from/--to with --wake or --sleep") +func validateModes(now bool, wake, sleep, from, to string) error { + modes := 0 + if now { + modes++ + } + if wake != "" || sleep != "" { + modes++ + } + if from != "" || to != "" { + modes++ + } + if modes > 1 { + return fmt.Errorf("cannot combine --now, --wake, --sleep, and --from/--to") + } + if modes == 0 { + return fmt.Errorf("must specify one of --now, --wake, --sleep, or --from/--to") } return nil } diff --git a/internal/validate/validate_test.go b/internal/validate/validate_test.go index 464832f..34912b0 100644 --- a/internal/validate/validate_test.go +++ b/internal/validate/validate_test.go @@ -2,6 +2,38 @@ package validate import "testing" +func TestValidateModes(t *testing.T) { + tests := []struct { + name string + now bool + wake string + sleep string + from string + to string + wantErr bool + }{ + {"now only", true, "", "", "", "", false}, + {"wake only", false, "07:00", "", "", "", false}, + {"sleep only", false, "", "22:00", "", "", false}, + {"window only", false, "", "", "22:00", "07:00", false}, + {"now with wake", true, "07:00", "", "", "", true}, + {"now with sleep", true, "", "22:00", "", "", true}, + {"now with window", true, "", "", "22:00", "07:00", true}, + {"wake with window", false, "07:00", "", "22:00", "07:00", true}, + {"sleep with window", false, "", "22:00", "22:00", "07:00", true}, + {"no mode", false, "", "", "", "", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateModes(tt.now, tt.wake, tt.sleep, tt.from, tt.to) + if (err != nil) != tt.wantErr { + t.Errorf("validateModes() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + func TestValidateWakeTime(t *testing.T) { tests := []struct { name string @@ -162,6 +194,7 @@ func TestValidateWindow(t *testing.T) { {"invalid --to format", Config{FromTime: "22:00", ToTime: "7am"}, true}, {"window mixed with --wake", Config{FromTime: "22:00", ToTime: "07:00", WakeTime: "07:00"}, true}, {"window mixed with --sleep", Config{FromTime: "22:00", ToTime: "07:00", SleepTime: "22:00"}, true}, + {"window mixed with --now", Config{FromTime: "22:00", ToTime: "07:00", Now: true}, true}, } for _, tt := range tests { diff --git a/main.go b/main.go index af3856f..ee4a5a2 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ const version = "0.1.0" func main() { var ( + nowFlag bool wakeFlag string sleepFlag string fromFlag string @@ -23,12 +24,13 @@ func main() { versionFlag bool ) + pflag.BoolVarP(&nowFlag, "now", "n", false, "Calculate wake times from current time") pflag.StringVarP(&wakeFlag, "wake", "w", "", "Calculate bedtimes from wake time (HH:MM)") pflag.StringVarP(&sleepFlag, "sleep", "s", "", "Calculate wake times from sleep time (HH:MM)") pflag.StringVarP(&fromFlag, "from", "f", "", "Window sleep time (HH:MM), use with --to") pflag.StringVarP(&toFlag, "to", "t", "", "Window wake time (HH:MM), use with --from") pflag.IntVarP(&bufferFlag, "buffer", "b", 15, "Fall asleep buffer in minutes") - pflag.IntVarP(&cyclesMinFlag, "cycles-min", "n", 4, "Minimum cycles to show") + pflag.IntVarP(&cyclesMinFlag, "cycles-min", "m", 4, "Minimum cycles to show") pflag.IntVarP(&cyclesMaxFlag, "cycles-max", "x", 6, "Maximum cycles to show") pflag.BoolVarP(&goodNightFlag, "good-night", "", false, "Display a random good night art") pflag.BoolVarP(&versionFlag, "version", "v", false, "Print version") @@ -45,7 +47,7 @@ func main() { os.Exit(0) } - if err := validateAndSelectMode(wakeFlag, sleepFlag, fromFlag, toFlag, bufferFlag, cyclesMinFlag, cyclesMaxFlag); err != nil { + if err := validateAndSelectMode(nowFlag, wakeFlag, sleepFlag, fromFlag, toFlag, bufferFlag, cyclesMinFlag, cyclesMaxFlag); err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } diff --git a/modes.go b/modes.go index b62f8b2..3308701 100644 --- a/modes.go +++ b/modes.go @@ -9,10 +9,12 @@ import ( ) func validateAndSelectMode( + nowFlag bool, wakeFlag, sleepFlag, fromFlag, toFlag string, bufferFlag, cyclesMinFlag, cyclesMaxFlag int, ) error { cfg := validate.Config{ + Now: nowFlag, WakeTime: wakeFlag, SleepTime: sleepFlag, FromTime: fromFlag, @@ -27,6 +29,9 @@ func validateAndSelectMode( buffer := time.Duration(bufferFlag) * time.Minute + if nowFlag { + return runNowMode(buffer, cyclesMinFlag, cyclesMaxFlag) + } if fromFlag != "" && toFlag != "" { return runWindowMode(fromFlag, toFlag, buffer) } @@ -39,6 +44,18 @@ func validateAndSelectMode( return fmt.Errorf("no valid mode selected") } +func runNowMode(buffer time.Duration, minCycles, maxCycles int) error { + now := time.Now() + wakeTimes := cycle.CalculateWakeTimes(now, buffer, minCycles, maxCycles) + + fmt.Printf("If you go to sleep now at %s:\n", now.Format("15:04")) + for i, wakeTime := range wakeTimes { + cycleCount := minCycles + i + fmt.Printf(" - For %d cycles, wake up at %s\n", cycleCount, wakeTime.Format("15:04")) + } + return nil +} + func runWindowMode(from, to string, buffer time.Duration) error { fromTime, err := time.Parse("15:04", validate.NormalizeHour(from)) if err != nil {