Author: Kevin Schoon [kevinschoon@gmail.com]
Hash: 793dd59baeb70734b56f1a709e1d9f9ac93c1946
Timestamp: Fri, 25 Jan 2019 05:04:41 +0000 (5 years ago)

+146 -84 +/-3 browse
refacort db & add fix begin/create commands
1diff --git a/main.go b/main.go
2index f8a6891..f283bc7 100644
3--- a/main.go
4+++ b/main.go
5 @@ -1,7 +1,9 @@
6 package main
7
8 import (
9+ "database/sql"
10 "encoding/json"
11+ "fmt"
12 "os"
13 "sort"
14 "time"
15 @@ -30,6 +32,14 @@ func start(path *string) func(*cli.Cmd) {
16 NPomodoros: *pomodoros,
17 Duration: parsed,
18 }
19+ maybe(db.With(func(tx *sql.Tx) error {
20+ id, err := db.CreateTask(tx, *task)
21+ if err != nil {
22+ return err
23+ }
24+ task.ID = id
25+ return nil
26+ }))
27 runner, err := NewTaskRunner(task, db, NewXnotifier(*path+"/icon.png"))
28 maybe(err)
29 server, err := NewServer(*path+"/pomo.sock", runner)
30 @@ -63,39 +73,51 @@ func create(path *string) func(*cli.Cmd) {
31 NPomodoros: *pomodoros,
32 Duration: parsed,
33 }
34- taskID, err := db.CreateTask(*task)
35- maybe(err)
36+ maybe(db.With(func(tx *sql.Tx) error {
37+ taskId, err := db.CreateTask(tx, *task)
38+ if err != nil {
39+ return err
40+ }
41+ fmt.Println(taskId)
42+ return nil
43+ }))
44 }
45 }
46 }
47
48 func begin(path *string) func(*cli.Cmd) {
49 return func(cmd *cli.Cmd) {
50- cmd.Spec = "ID"
51- var jobId = cmd.IntArg("ID", -1, "ID of Pomodoro to begin")
52+ cmd.Spec = "[OPTIONS] TASK_ID"
53+ var (
54+ taskId = cmd.IntArg("TASK_ID", -1, "ID of Pomodoro to begin")
55+ )
56
57 cmd.Action = func() {
58 db, err := NewStore(*path)
59 maybe(err)
60 defer db.Close()
61- tasks, err := db.ReadTasks()
62- maybe(err)
63- task := &Task{}
64- for _, task = range tasks {
65- if task.ID == *jobId {
66- break
67+ var task *Task
68+ maybe(db.With(func(tx *sql.Tx) error {
69+ read, err := db.ReadTask(tx, *taskId)
70+ if err != nil {
71+ return err
72 }
73- }
74- if task.ID == *jobId {
75- runner, err := NewTaskRunner(task, db, NewXnotifier(*path+"/icon.png"))
76- maybe(err)
77- server, err := NewServer(*path+"/pomo.sock", runner)
78- maybe(err)
79- server.Start()
80- defer server.Stop()
81- runner.Start()
82- startUI(runner)
83- }
84+ task = read
85+ err = db.DeletePomodoros(tx, *taskId)
86+ if err != nil {
87+ return err
88+ }
89+ task.Pomodoros = []*Pomodoro{}
90+ return nil
91+ }))
92+ runner, err := NewTaskRunner(task, db, NewXnotifier(*path+"/icon.png"))
93+ maybe(err)
94+ server, err := NewServer(*path+"/pomo.sock", runner)
95+ maybe(err)
96+ server.Start()
97+ defer server.Stop()
98+ runner.Start()
99+ startUI(runner)
100 }
101 }
102 }
103 @@ -128,24 +150,27 @@ func list(path *string) func(*cli.Cmd) {
104 db, err := NewStore(*path)
105 maybe(err)
106 defer db.Close()
107- tasks, err := db.ReadTasks()
108- maybe(err)
109- if *assend {
110- sort.Sort(sort.Reverse(ByID(tasks)))
111- }
112- if !*all {
113- tasks = After(time.Now().Add(-duration), tasks)
114- }
115- if *limit > 0 && (len(tasks) > *limit) {
116- tasks = tasks[0:*limit]
117- }
118- if *asJSON {
119- maybe(json.NewEncoder(os.Stdout).Encode(tasks))
120- return
121- }
122- config, err := NewConfig(*path + "/config.json")
123- maybe(err)
124- summerizeTasks(config, tasks)
125+ maybe(db.With(func(tx *sql.Tx) error {
126+ tasks, err := db.ReadTasks(tx)
127+ maybe(err)
128+ if *assend {
129+ sort.Sort(sort.Reverse(ByID(tasks)))
130+ }
131+ if !*all {
132+ tasks = After(time.Now().Add(-duration), tasks)
133+ }
134+ if *limit > 0 && (len(tasks) > *limit) {
135+ tasks = tasks[0:*limit]
136+ }
137+ if *asJSON {
138+ maybe(json.NewEncoder(os.Stdout).Encode(tasks))
139+ return nil
140+ }
141+ config, err := NewConfig(*path + "/config.json")
142+ maybe(err)
143+ summerizeTasks(config, tasks)
144+ return nil
145+ }))
146 }
147 }
148 }
149 @@ -158,7 +183,9 @@ func _delete(path *string) func(*cli.Cmd) {
150 db, err := NewStore(*path)
151 maybe(err)
152 defer db.Close()
153- maybe(db.DeleteTask(*taskID))
154+ maybe(db.With(func(tx *sql.Tx) error {
155+ return db.DeleteTask(tx, *taskID)
156+ }))
157 }
158 }
159 }
160 diff --git a/runner.go b/runner.go
161index 9823484..97869d6 100644
162--- a/runner.go
163+++ b/runner.go
164 @@ -1,6 +1,7 @@
165 package main
166
167 import (
168+ "database/sql"
169 "time"
170 )
171
172 @@ -20,12 +21,8 @@ type TaskRunner struct {
173 }
174
175 func NewTaskRunner(task *Task, store *Store, notifier Notifier) (*TaskRunner, error) {
176- taskID, err := store.CreateTask(*task)
177- if err != nil {
178- return nil, err
179- }
180 tr := &TaskRunner{
181- taskID: taskID,
182+ taskID: task.ID,
183 taskMessage: task.Message,
184 nPomodoros: task.NPomodoros,
185 origDuration: task.Duration,
186 @@ -89,7 +86,9 @@ func (t *TaskRunner) run() error {
187 goto loop
188 }
189 pomodoro.End = time.Now()
190- err := t.store.CreatePomodoro(t.taskID, *pomodoro)
191+ err := t.store.With(func(tx *sql.Tx) error {
192+ return t.store.CreatePomodoro(tx, t.taskID, *pomodoro)
193+ })
194 if err != nil {
195 return err
196 }
197 diff --git a/store.go b/store.go
198index a56ed75..0152fbe 100644
199--- a/store.go
200+++ b/store.go
201 @@ -12,6 +12,8 @@ import (
202 // 2018-01-16 19:05:21.752851759+08:00
203 const datetimeFmt = "2006-01-02 15:04:05.999999999-07:00"
204
205+ type StoreFunc func(tx *sql.Tx) error
206+
207 type Store struct {
208 db *sql.DB
209 }
210 @@ -25,39 +27,45 @@ func NewStore(path string) (*Store, error) {
211 return &Store{db: db}, nil
212 }
213
214- func (s Store) CreateTask(task Task) (int, error) {
215- var taskID int
216+ // With applies all of the given functions with
217+ // a single transaction, rolling back on failure
218+ // and commiting on success.
219+ func (s Store) With(fns ...func(tx *sql.Tx) error) error {
220 tx, err := s.db.Begin()
221 if err != nil {
222- return -1, err
223+ return err
224 }
225- _, err = tx.Exec(
226+ for _, fn := range fns {
227+ err = fn(tx)
228+ if err != nil {
229+ tx.Rollback()
230+ return err
231+ }
232+ }
233+ return tx.Commit()
234+ }
235+
236+ func (s Store) CreateTask(tx *sql.Tx, task Task) (int, error) {
237+ var taskID int
238+ _, err := tx.Exec(
239 "INSERT INTO task (message,pomodoros,duration,tags) VALUES ($1,$2,$3,$4)",
240 task.Message, task.NPomodoros, task.Duration.String(), strings.Join(task.Tags, ","))
241 if err != nil {
242- tx.Rollback()
243 return -1, err
244 }
245 err = tx.QueryRow("SELECT last_insert_rowid() FROM task").Scan(&taskID)
246 if err != nil {
247- tx.Rollback()
248 return -1, err
249 }
250- return taskID, tx.Commit()
251- }
252-
253- func (s Store) CreatePomodoro(taskID int, pomodoro Pomodoro) error {
254- _, err := s.db.Exec(
255- `INSERT INTO pomodoro (task_id, start, end) VALUES ($1, $2, $3)`,
256- taskID,
257- pomodoro.Start,
258- pomodoro.End,
259- )
260- return err
261+ err = tx.QueryRow("SELECT last_insert_rowid() FROM task").Scan(&taskID)
262+ if err != nil {
263+ return -1, err
264+ }
265+ return taskID, nil
266 }
267
268- func (s Store) ReadTasks() ([]*Task, error) {
269- rows, err := s.db.Query(`SELECT rowid,message,pomodoros,duration,tags FROM task`)
270+ func (s Store) ReadTasks(tx *sql.Tx) ([]*Task, error) {
271+ rows, err := tx.Query(`SELECT rowid,message,pomodoros,duration,tags FROM task`)
272 if err != nil {
273 return nil, err
274 }
275 @@ -77,7 +85,7 @@ func (s Store) ReadTasks() ([]*Task, error) {
276 if tags != "" {
277 task.Tags = strings.Split(tags, ",")
278 }
279- pomodoros, err := s.ReadPomodoros(task.ID)
280+ pomodoros, err := s.ReadPomodoros(tx, task.ID)
281 if err != nil {
282 return nil, err
283 }
284 @@ -89,8 +97,49 @@ func (s Store) ReadTasks() ([]*Task, error) {
285 return tasks, nil
286 }
287
288- func (s Store) ReadPomodoros(taskID int) ([]*Pomodoro, error) {
289- rows, err := s.db.Query(`SELECT start,end FROM pomodoro WHERE task_id = $1`, &taskID)
290+ func (s Store) DeleteTask(tx *sql.Tx, taskID int) error {
291+ _, err := tx.Exec("DELETE FROM task WHERE rowid = $1", &taskID)
292+ if err != nil {
293+ return err
294+ }
295+ _, err = tx.Exec("DELETE FROM pomodoro WHERE task_id = $1", &taskID)
296+ if err != nil {
297+ return err
298+ }
299+ return nil
300+ }
301+
302+ func (s Store) ReadTask(tx *sql.Tx, taskID int) (*Task, error) {
303+ task := &Task{}
304+ var (
305+ tags string
306+ strDuration string
307+ )
308+ err := tx.QueryRow(`SELECT rowid,message,pomodoros,duration,tags FROM task WHERE rowid = $1`, &taskID).
309+ Scan(&task.ID, &task.Message, &task.NPomodoros, &strDuration, &tags)
310+ if err != nil {
311+ return nil, err
312+ }
313+ duration, _ := time.ParseDuration(strDuration)
314+ task.Duration = duration
315+ if tags != "" {
316+ task.Tags = strings.Split(tags, ",")
317+ }
318+ return task, nil
319+ }
320+
321+ func (s Store) CreatePomodoro(tx *sql.Tx, taskID int, pomodoro Pomodoro) error {
322+ _, err := tx.Exec(
323+ `INSERT INTO pomodoro (task_id, start, end) VALUES ($1, $2, $3)`,
324+ taskID,
325+ pomodoro.Start,
326+ pomodoro.End,
327+ )
328+ return err
329+ }
330+
331+ func (s Store) ReadPomodoros(tx *sql.Tx, taskID int) ([]*Pomodoro, error) {
332+ rows, err := tx.Query(`SELECT start,end FROM pomodoro WHERE task_id = $1`, &taskID)
333 if err != nil {
334 return nil, err
335 }
336 @@ -114,22 +163,9 @@ func (s Store) ReadPomodoros(taskID int) ([]*Pomodoro, error) {
337 return pomodoros, nil
338 }
339
340- func (s Store) DeleteTask(taskID int) error {
341- tx, err := s.db.Begin()
342- if err != nil {
343- return err
344- }
345- _, err = tx.Exec("DELETE FROM task WHERE rowid = $1", &taskID)
346- if err != nil {
347- tx.Rollback()
348- return err
349- }
350- _, err = tx.Exec("DELETE FROM pomodoro WHERE task_id = $1", &taskID)
351- if err != nil {
352- tx.Rollback()
353- return err
354- }
355- return tx.Commit()
356+ func (s Store) DeletePomodoros(tx *sql.Tx, taskID int) error {
357+ _, err := tx.Exec("DELETE FROM pomodoro WHERE task_id = $1", &taskID)
358+ return err
359 }
360
361 func (s Store) Close() error { return s.db.Close() }