aboutsummaryrefslogtreecommitdiff
path: root/main.go
blob: 24cfb3c234fc71e98f4b8de831f0ced7cbdcd448 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"time"
)

type Opt struct {
	Work  time.Duration
	Short time.Duration
	Long  time.Duration
	Day   time.Duration
	Runs  int
	run   int
}

type display struct {
	s string
	d time.Duration
}

type stateFn func(context.Context, chan display) stateFn

func (o *Opt) doWork(ctx context.Context, c chan display) stateFn {
	t := time.NewTimer(o.Work)
	defer t.Stop()
	c <- display{"Work", o.Work}
	select {
	case <-ctx.Done():
		return nil
	case <-t.C:
		return o.doBreak
	}
}

func (o *Opt) doBreak(_ context.Context, _ chan display) stateFn {
	if o.run++; o.run%o.Runs == 0 {
		return o.longBreak
	}
	return o.shortBreak
}

func (o *Opt) shortBreak(ctx context.Context, c chan display) stateFn {
	t := time.NewTimer(o.Short)
	defer t.Stop()
	c <- display{"Short", o.Short}
	select {
	case <-ctx.Done():
		return nil
	case <-t.C:
		return o.doWork
	}
}

func (o *Opt) longBreak(ctx context.Context, c chan display) stateFn {
	t := time.NewTimer(o.Long)
	defer t.Stop()
	c <- display{"Long", o.Long}
	select {
	case <-ctx.Done():
		return nil
	case <-t.C:
		return o.doWork
	}
}

func notifier(ctx context.Context) chan display {
	c := make(chan display)
	go func() {
		defer close(c)
		for v := range c {
			select {
			case <-ctx.Done():
				return
			default:
				fmt.Println(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)
	c := notifier(ctx)
	for s := o.doWork; s != nil; s = s(ctx, c) {
	}
}

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()
}