package main import ( "context" "flag" "log" "os" "os/signal" "time" ) type Opt struct { Work time.Duration Short time.Duration Long time.Duration Day time.Duration Runs int run int do chan do } type do struct { s string d time.Duration } type stateFn func(context.Context) stateFn func (o *Opt) doWork(ctx context.Context) stateFn { timer := time.NewTimer(o.Work) defer timer.Stop() o.do <- do{"work", o.Work} select { case <-ctx.Done(): return nil case <-timer.C: return o.doBreak } } func (o *Opt) doBreak(_ context.Context) stateFn { if o.run++; o.run%o.Runs == 0 { return o.longBreak } return o.shortBreak } func (o *Opt) shortBreak(ctx context.Context) stateFn { timer := time.NewTimer(o.Short) defer timer.Stop() o.do <- do{"short", o.Short} select { case <-ctx.Done(): return nil case <-timer.C: return o.doWork } } func (o *Opt) longBreak(ctx context.Context) stateFn { timer := time.NewTimer(o.Long) defer timer.Stop() o.do <- do{"long", o.Long} select { case <-ctx.Done(): return nil case <-timer.C: return o.doWork } } func notifier(ctx context.Context) chan do { c := make(chan do) go func() { defer close(c) for v := range c { select { case <-ctx.Done(): return default: count(ctx, v.s, v.d) } } }() return c } func atInterrupt(cancel context.CancelFunc) { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { <-c cancel() }() } func (o *Opt) pomodoro() { defer func(t time.Time) { log.Printf("total %v", time.Since(t)) }(time.Now()) ctx, cancel := context.WithTimeout(context.Background(), o.Day) defer cancel() atInterrupt(cancel) o.do = notifier(ctx) for s := o.doWork; s != nil; s = s(ctx) { } } const timeBase = time.Second func main() { var o Opt flag.DurationVar(&o.Work, "work", 25*timeBase, "work time") flag.DurationVar(&o.Short, "short", 5*timeBase, "short break") flag.DurationVar(&o.Long, "long", 15*timeBase, "long break") flag.DurationVar(&o.Day, "day", 600*timeBase, "work day") flag.IntVar(&o.Runs, "runs", 4, "work runs") flag.Parse() o.pomodoro() }