kevinschoon-pomo/pkg/internal/runner.go

202 lines
4.4 KiB
Go
Raw Normal View History

2020-09-08 18:35:47 +02:00
package pomo
2018-01-20 13:03:23 +01:00
import (
"database/sql"
2022-05-31 08:20:58 +02:00
"fmt"
"os"
"os/exec"
"sync"
2018-01-20 13:03:23 +01:00
"time"
)
type TaskRunner struct {
2018-01-27 16:58:56 +01:00
count int
taskID int
taskMessage string
nPomodoros int
origDuration time.Duration
state State
store *Store
started time.Time
stopped time.Time
2018-01-27 16:58:56 +01:00
pause chan bool
toggle chan bool
notifier Notifier
duration time.Duration
mu sync.Mutex
2022-05-31 08:20:58 +02:00
onEvent []string
}
2020-09-06 05:49:42 +02:00
func NewMockedTaskRunner(task *Task, store *Store, notifier Notifier) (*TaskRunner, error) {
tr := &TaskRunner{
taskID: task.ID,
taskMessage: task.Message,
nPomodoros: task.NPomodoros,
origDuration: task.Duration,
store: store,
state: CREATED,
2020-09-06 05:49:42 +02:00
pause: make(chan bool),
toggle: make(chan bool),
notifier: notifier,
duration: task.Duration,
}
return tr, nil
}
func NewTaskRunner(task *Task, config *Config) (*TaskRunner, error) {
store, err := NewStore(config.DBPath)
if err != nil {
return nil, err
}
tr := &TaskRunner{
count: len(task.Pomodoros),
taskID: task.ID,
2018-01-27 16:58:56 +01:00
taskMessage: task.Message,
nPomodoros: task.NPomodoros,
origDuration: task.Duration,
store: store,
state: State(0),
pause: make(chan bool),
toggle: make(chan bool),
notifier: NewXnotifier(config.IconPath),
2018-01-27 16:58:56 +01:00
duration: task.Duration,
2022-05-31 08:20:58 +02:00
onEvent: config.OnEvent,
}
return tr, nil
}
2018-01-26 16:07:38 +01:00
func (t *TaskRunner) Start() {
go t.run()
}
func (t *TaskRunner) TimeRemaining() time.Duration {
return (t.duration - time.Since(t.started)).Truncate(time.Second)
}
func (t *TaskRunner) TimePauseDuration() time.Duration {
return (time.Since(t.stopped)).Truncate(time.Second)
}
func (t *TaskRunner) SetState(state State) {
t.state = state
// execute onEvent command if variable is set
if t.onEvent != nil {
2022-06-01 03:06:43 +02:00
go t.runOnEvent()
}
}
2022-05-31 08:20:58 +02:00
// execute script command specified by `onEvent` on state change
func (t *TaskRunner) runOnEvent() error {
var cmd *exec.Cmd
// parse command arguments
numArgs := len(t.onEvent)
app := t.onEvent[0]
if numArgs > 1 {
args := t.onEvent[1:(numArgs + 1)]
cmd = exec.Command(app, args...)
} else {
cmd = exec.Command(app)
}
// set state in command environment
2022-05-31 08:20:58 +02:00
cmd.Env = append(os.Environ(),
fmt.Sprintf("POMO_STATE=%s", t.state),
)
// run command
2022-05-31 08:20:58 +02:00
err := cmd.Run()
if err != nil {
return err
}
return nil
}
2018-01-26 16:07:38 +01:00
func (t *TaskRunner) run() error {
2018-01-27 16:58:56 +01:00
for t.count < t.nPomodoros {
2018-01-26 16:07:38 +01:00
// Create a new pomodoro where we
// track the start / end time of
// of this session.
pomodoro := &Pomodoro{}
// Start this pomodoro
pomodoro.Start = time.Now()
// Set state to RUNNIN
t.SetState(RUNNING)
2018-01-26 16:07:38 +01:00
// Create a new timer
timer := time.NewTimer(t.duration)
// Record our started time
t.started = pomodoro.Start
loop:
select {
case <-timer.C:
t.stopped = time.Now()
2018-01-27 16:58:56 +01:00
t.count++
2018-01-26 16:07:38 +01:00
case <-t.toggle:
// Catch any toggles when we
// are not expecting them
goto loop
case <-t.pause:
timer.Stop()
2018-01-27 16:58:56 +01:00
// Record the remaining time of the current pomodoro
2018-01-26 16:07:38 +01:00
remaining := t.TimeRemaining()
// Change state to PAUSED
t.SetState(PAUSED)
2018-01-26 16:07:38 +01:00
// Wait for the user to press [p]
<-t.pause
2018-01-27 16:58:56 +01:00
// Resume the timer with previous
2018-01-26 16:07:38 +01:00
// remaining time
timer.Reset(remaining)
2018-01-27 16:58:56 +01:00
// Change duration
2018-01-26 16:07:38 +01:00
t.started = time.Now()
t.duration = remaining
// Restore state to RUNNING
t.SetState(RUNNING)
2018-01-26 16:07:38 +01:00
goto loop
}
pomodoro.End = time.Now()
err := t.store.With(func(tx *sql.Tx) error {
return t.store.CreatePomodoro(tx, t.taskID, *pomodoro)
})
2018-01-26 16:07:38 +01:00
if err != nil {
return err
}
2018-01-27 16:58:56 +01:00
// All pomodoros completed
if t.count == t.nPomodoros {
break
}
t.SetState(BREAKING)
2018-01-27 17:42:13 +01:00
t.notifier.Notify("Pomo", "It is time to take a break!")
2018-01-27 16:58:56 +01:00
// Reset the duration incase it
// was paused.
t.duration = t.origDuration
// User concludes the break
<-t.toggle
2018-01-26 16:07:38 +01:00
}
t.notifier.Notify("Pomo", "Pomo session has completed!")
t.SetState(COMPLETE)
2018-01-26 16:07:38 +01:00
return nil
}
func (t *TaskRunner) Toggle() {
t.mu.Lock()
defer t.mu.Unlock()
if t.state == BREAKING {
t.toggle <- true
}
2018-01-26 16:07:38 +01:00
}
func (t *TaskRunner) Pause() {
t.mu.Lock()
defer t.mu.Unlock()
if t.state == PAUSED || t.state == RUNNING {
t.pause <- true
}
2018-01-26 16:07:38 +01:00
}
func (t *TaskRunner) Status() *Status {
return &Status{
2021-09-01 14:56:22 +02:00
State: t.state,
Count: t.count,
NPomodoros: t.nPomodoros,
Remaining: t.TimeRemaining(),
Pauseduration: t.TimePauseDuration(),
}
}