1 | package main
|
2 |
|
3 | import (
|
4 | "database/sql"
|
5 | "encoding/json"
|
6 | "fmt"
|
7 | "os"
|
8 | "sort"
|
9 | "time"
|
10 |
|
11 | cli "github.com/jawher/mow.cli"
|
12 | )
|
13 |
|
14 | func start(config *Config) func(*cli.Cmd) {
|
15 | return func(cmd *cli.Cmd) {
|
16 | cmd.Spec = "[OPTIONS] MESSAGE"
|
17 | var (
|
18 | duration = cmd.StringOpt("d duration", "25m", "duration of each stent")
|
19 | pomodoros = cmd.IntOpt("p pomodoros", 4, "number of pomodoros")
|
20 | message = cmd.StringArg("MESSAGE", "", "descriptive name of the given task")
|
21 | tags = cmd.StringsOpt("t tag", []string{}, "tags associated with this task")
|
22 | )
|
23 | cmd.Action = func() {
|
24 | parsed, err := time.ParseDuration(*duration)
|
25 | maybe(err)
|
26 | db, err := NewStore(config.DBPath)
|
27 | maybe(err)
|
28 | defer db.Close()
|
29 | task := &Task{
|
30 | Message: *message,
|
31 | Tags: *tags,
|
32 | NPomodoros: *pomodoros,
|
33 | Duration: parsed,
|
34 | }
|
35 | maybe(db.With(func(tx *sql.Tx) error {
|
36 | id, err := db.CreateTask(tx, *task)
|
37 | if err != nil {
|
38 | return err
|
39 | }
|
40 | task.ID = id
|
41 | return nil
|
42 | }))
|
43 | runner, err := NewTaskRunner(task, db, NewXnotifier(config.IconPath))
|
44 | maybe(err)
|
45 | server, err := NewServer(config.SocketPath, runner)
|
46 | maybe(err)
|
47 | server.Start()
|
48 | defer server.Stop()
|
49 | runner.Start()
|
50 | startUI(runner)
|
51 | }
|
52 | }
|
53 | }
|
54 |
|
55 | func create(config *Config) func(*cli.Cmd) {
|
56 | return func(cmd *cli.Cmd) {
|
57 | cmd.Spec = "[OPTIONS] MESSAGE"
|
58 | var (
|
59 | duration = cmd.StringOpt("d duration", "25m", "duration of each stent")
|
60 | pomodoros = cmd.IntOpt("p pomodoros", 4, "number of pomodoros")
|
61 | message = cmd.StringArg("MESSAGE", "", "descriptive name of the given task")
|
62 | tags = cmd.StringsOpt("t tag", []string{}, "tags associated with this task")
|
63 | )
|
64 | cmd.Action = func() {
|
65 | parsed, err := time.ParseDuration(*duration)
|
66 | maybe(err)
|
67 | db, err := NewStore(config.DBPath)
|
68 | maybe(err)
|
69 | defer db.Close()
|
70 | task := &Task{
|
71 | Message: *message,
|
72 | Tags: *tags,
|
73 | NPomodoros: *pomodoros,
|
74 | Duration: parsed,
|
75 | }
|
76 | maybe(db.With(func(tx *sql.Tx) error {
|
77 | taskId, err := db.CreateTask(tx, *task)
|
78 | if err != nil {
|
79 | return err
|
80 | }
|
81 | fmt.Println(taskId)
|
82 | return nil
|
83 | }))
|
84 | }
|
85 | }
|
86 | }
|
87 |
|
88 | func begin(config *Config) func(*cli.Cmd) {
|
89 | return func(cmd *cli.Cmd) {
|
90 | cmd.Spec = "[OPTIONS] TASK_ID"
|
91 | var (
|
92 | taskId = cmd.IntArg("TASK_ID", -1, "ID of Pomodoro to begin")
|
93 | )
|
94 |
|
95 | cmd.Action = func() {
|
96 | db, err := NewStore(config.DBPath)
|
97 | maybe(err)
|
98 | defer db.Close()
|
99 | var task *Task
|
100 | maybe(db.With(func(tx *sql.Tx) error {
|
101 | read, err := db.ReadTask(tx, *taskId)
|
102 | if err != nil {
|
103 | return err
|
104 | }
|
105 | task = read
|
106 | err = db.DeletePomodoros(tx, *taskId)
|
107 | if err != nil {
|
108 | return err
|
109 | }
|
110 | task.Pomodoros = []*Pomodoro{}
|
111 | return nil
|
112 | }))
|
113 | runner, err := NewTaskRunner(task, db, NewXnotifier(config.IconPath))
|
114 | maybe(err)
|
115 | server, err := NewServer(config.SocketPath, runner)
|
116 | maybe(err)
|
117 | server.Start()
|
118 | defer server.Stop()
|
119 | runner.Start()
|
120 | startUI(runner)
|
121 | }
|
122 | }
|
123 | }
|
124 |
|
125 | func initialize(config *Config) func(*cli.Cmd) {
|
126 | return func(cmd *cli.Cmd) {
|
127 | cmd.Spec = "[OPTIONS]"
|
128 | cmd.Action = func() {
|
129 | db, err := NewStore(config.DBPath)
|
130 | maybe(err)
|
131 | defer db.Close()
|
132 | maybe(initDB(db))
|
133 | }
|
134 | }
|
135 | }
|
136 |
|
137 | func list(config *Config) func(*cli.Cmd) {
|
138 | return func(cmd *cli.Cmd) {
|
139 | cmd.Spec = "[OPTIONS]"
|
140 | var (
|
141 | asJSON = cmd.BoolOpt("json", false, "output task history as JSON")
|
142 | assend = cmd.BoolOpt("assend", false, "sort tasks assending in age")
|
143 | all = cmd.BoolOpt("a all", true, "output all tasks")
|
144 | limit = cmd.IntOpt("n limit", 0, "limit the number of results by n")
|
145 | duration = cmd.StringOpt("d duration", "24h", "show tasks within this duration")
|
146 | )
|
147 | cmd.Action = func() {
|
148 | duration, err := time.ParseDuration(*duration)
|
149 | maybe(err)
|
150 | db, err := NewStore(config.DBPath)
|
151 | maybe(err)
|
152 | defer db.Close()
|
153 | maybe(db.With(func(tx *sql.Tx) error {
|
154 | tasks, err := db.ReadTasks(tx)
|
155 | maybe(err)
|
156 | if *assend {
|
157 | sort.Sort(sort.Reverse(ByID(tasks)))
|
158 | }
|
159 | if !*all {
|
160 | tasks = After(time.Now().Add(-duration), tasks)
|
161 | }
|
162 | if *limit > 0 && (len(tasks) > *limit) {
|
163 | tasks = tasks[0:*limit]
|
164 | }
|
165 | if *asJSON {
|
166 | maybe(json.NewEncoder(os.Stdout).Encode(tasks))
|
167 | return nil
|
168 | }
|
169 | maybe(err)
|
170 | summerizeTasks(config, tasks)
|
171 | return nil
|
172 | }))
|
173 | }
|
174 | }
|
175 | }
|
176 |
|
177 | func _delete(config *Config) func(*cli.Cmd) {
|
178 | return func(cmd *cli.Cmd) {
|
179 | cmd.Spec = "[OPTIONS] TASK_ID"
|
180 | var taskID = cmd.IntArg("TASK_ID", -1, "task to delete")
|
181 | cmd.Action = func() {
|
182 | db, err := NewStore(config.DBPath)
|
183 | maybe(err)
|
184 | defer db.Close()
|
185 | maybe(db.With(func(tx *sql.Tx) error {
|
186 | return db.DeleteTask(tx, *taskID)
|
187 | }))
|
188 | }
|
189 | }
|
190 | }
|
191 |
|
192 | func _status(config *Config) func(*cli.Cmd) {
|
193 | return func(cmd *cli.Cmd) {
|
194 | cmd.Spec = "[OPTIONS]"
|
195 | cmd.Action = func() {
|
196 | client, err := NewClient(config.SocketPath)
|
197 | if err != nil {
|
198 | outputStatus(Status{})
|
199 | return
|
200 | }
|
201 | defer client.Close()
|
202 | status, err := client.Status()
|
203 | maybe(err)
|
204 | outputStatus(*status)
|
205 | }
|
206 | }
|
207 | }
|
208 |
|
209 | func _config(config *Config) func(*cli.Cmd) {
|
210 | return func(cmd *cli.Cmd) {
|
211 | cmd.Spec = "[OPTIONS]"
|
212 | cmd.Action = func() {
|
213 | maybe(json.NewEncoder(os.Stdout).Encode(config))
|
214 | }
|
215 | }
|
216 | }
|
217 |
|
218 | func main() {
|
219 | app := cli.App("pomo", "Pomodoro CLI")
|
220 | app.LongDesc = "Pomo helps you track what you did, how long it took you to do it, and how much effort you expect it to take."
|
221 | app.Spec = "[OPTIONS]"
|
222 | var (
|
223 | config = &Config{}
|
224 | path = app.StringOpt("p path", defaultConfigPath(), "path to the pomo config directory")
|
225 | )
|
226 | app.Before = func() {
|
227 | maybe(LoadConfig(*path, config))
|
228 | }
|
229 | app.Version("v version", Version)
|
230 | app.Command("start s", "start a new task", start(config))
|
231 | app.Command("init", "initialize the sqlite database", initialize(config))
|
232 | app.Command("config cf", "display the current configuration", _config(config))
|
233 | app.Command("create c", "create a new task without starting", create(config))
|
234 | app.Command("begin b", "begin requested pomodoro", begin(config))
|
235 | app.Command("list l", "list historical tasks", list(config))
|
236 | app.Command("delete d", "delete a stored task", _delete(config))
|
237 | app.Command("status st", "output the current status", _status(config))
|
238 | app.Run(os.Args)
|
239 | }
|