2019-02-21 02:44:07 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
2021-03-02 17:10:29 +00:00
|
|
|
timertxt "git.bullercodeworks.com/brian/go-timertxt"
|
2021-03-02 17:05:44 +00:00
|
|
|
userConfig "github.com/br0xen/user-config"
|
2019-02-21 02:44:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type AppState struct {
|
|
|
|
Name string
|
|
|
|
Version int
|
|
|
|
config *userConfig.Config
|
|
|
|
directory string
|
|
|
|
fileTimer string
|
|
|
|
fileDone string
|
|
|
|
fileReport string
|
|
|
|
ValidOperations map[string][]string
|
|
|
|
OpFuncs map[string]func([]string) int
|
|
|
|
|
|
|
|
TimerList *timertxt.TimerList
|
2019-02-22 23:56:15 +00:00
|
|
|
DoneList *timertxt.TimerList
|
|
|
|
screens []Screen
|
2019-02-21 02:44:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewApp() *AppState {
|
|
|
|
app := &AppState{Name: AppName, Version: AppVersion}
|
|
|
|
app.initialize()
|
|
|
|
app.doVersionCheck()
|
|
|
|
if err := app.LoadTimerList(); err != nil {
|
|
|
|
if len(os.Args) > 1 && os.Args[1] != "--reinit" {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return app
|
|
|
|
}
|
|
|
|
|
2019-02-22 23:56:15 +00:00
|
|
|
func (a *AppState) filterList(list *timertxt.TimerList, filter string) *timertxt.TimerList {
|
|
|
|
return list.Filter(a.getFilterPredicate(filter))
|
|
|
|
}
|
|
|
|
|
2019-02-21 02:44:07 +00:00
|
|
|
func (a *AppState) run(parms []string) int {
|
|
|
|
if len(parms) == 0 || parms[0] == "ui" {
|
|
|
|
// UI Mode
|
2019-02-22 23:56:15 +00:00
|
|
|
return uiLoop()
|
2019-02-21 02:44:07 +00:00
|
|
|
}
|
|
|
|
if fn, ok := a.OpFuncs[parms[0]]; ok {
|
|
|
|
return fn(parms[1:])
|
|
|
|
}
|
|
|
|
fmt.Println("Unknown Command")
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppState) getTimerFile() string {
|
|
|
|
return a.directory + a.fileTimer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppState) getDoneFile() string {
|
|
|
|
return a.directory + a.fileDone
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppState) getReportFile() string {
|
|
|
|
return a.directory + a.fileReport
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppState) addOperation(name string, desc []string, fn func([]string) int) {
|
|
|
|
a.ValidOperations[name] = desc
|
|
|
|
a.OpFuncs[name] = fn
|
|
|
|
}
|
|
|
|
|
2019-02-22 23:56:15 +00:00
|
|
|
func (a *AppState) getTimerString(timer timertxt.Timer) string {
|
|
|
|
return fmt.Sprintf("%d. %s", timer.Id, strings.TrimPrefix(timer.String(), "x "))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppState) getDoneTimerString(timer timertxt.Timer) string {
|
2019-04-22 15:29:50 +00:00
|
|
|
return fmt.Sprintf("--. %s", timer.String())
|
2019-02-22 23:56:15 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 02:44:07 +00:00
|
|
|
func (a *AppState) doVersionCheck() {
|
|
|
|
confVer, _ := a.config.GetInt("version")
|
|
|
|
for confVer < a.Version {
|
|
|
|
confVer = a.migrate(confVer, a.Version)
|
|
|
|
}
|
|
|
|
a.config.SetInt("version", confVer)
|
|
|
|
}
|
|
|
|
|
2020-05-29 22:24:05 +00:00
|
|
|
// Data Migrations
|
|
|
|
// 0 -> 1: Initialize Config
|
|
|
|
// 1 -> 2: No migration Needed
|
2019-02-21 02:44:07 +00:00
|
|
|
func (a *AppState) migrate(from, to int) int {
|
|
|
|
if from == to {
|
|
|
|
return to
|
|
|
|
}
|
|
|
|
switch from {
|
|
|
|
case 0:
|
|
|
|
a.initializeConfig()
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
// If we get all the way down here, we _must_ be done.
|
|
|
|
return to
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppState) initialize() {
|
|
|
|
var err error
|
|
|
|
a.config, err = userConfig.NewConfig(a.Name)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
a.ValidOperations = make(map[string][]string)
|
|
|
|
a.OpFuncs = make(map[string]func([]string) int)
|
2021-03-02 17:05:44 +00:00
|
|
|
a.addOperation("i3status",
|
|
|
|
[]string{
|
|
|
|
"i3status - Output JSON for i3status-rust to consume",
|
|
|
|
},
|
|
|
|
a.opI3Status,
|
|
|
|
)
|
|
|
|
a.addOperation("toggle",
|
|
|
|
[]string{
|
|
|
|
"toggle - Pause/Resume the most recent timer",
|
|
|
|
},
|
|
|
|
a.opToggleTimer,
|
|
|
|
)
|
2020-05-29 22:24:05 +00:00
|
|
|
a.addOperation("time",
|
|
|
|
[]string{
|
|
|
|
"time [contexts] [projects] - Only output the total time value",
|
|
|
|
" --a - Include done.txt file",
|
|
|
|
" [start] - List entries after this date",
|
|
|
|
" [end] - List entries before this date",
|
|
|
|
" [@contexts] - Filter entries with the given contexts",
|
|
|
|
" [+projects] - Filter entries with the given projects",
|
|
|
|
},
|
|
|
|
a.opShowTime,
|
|
|
|
)
|
2019-02-21 02:44:07 +00:00
|
|
|
a.addOperation("ls",
|
|
|
|
[]string{
|
2019-02-26 15:41:00 +00:00
|
|
|
"ls [--a] [start] [end] [contexts] [projects] - List Timers",
|
|
|
|
" --a - Include done.txt file",
|
|
|
|
" [start] - List entries after this date",
|
|
|
|
" [end] - List entries before this date",
|
|
|
|
" [@contexts] - List entries with the given contexts",
|
|
|
|
" [+projects] - List entries with the given projects",
|
2019-02-21 02:44:07 +00:00
|
|
|
},
|
|
|
|
a.opListTimers,
|
|
|
|
)
|
|
|
|
a.addOperation("lsa",
|
|
|
|
[]string{
|
2019-02-26 15:41:00 +00:00
|
|
|
"lsa [start] [end] [contexts] [projects] - Shortcut for 'ls --a'",
|
2019-02-21 02:44:07 +00:00
|
|
|
},
|
|
|
|
func(args []string) int {
|
2019-02-26 15:41:00 +00:00
|
|
|
return a.opListTimers(append([]string{"--a"}, args...))
|
2019-02-21 02:44:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
a.addOperation("start",
|
|
|
|
[]string{
|
|
|
|
"start [time] [@contexts...] [+projects...] [tag:value...]",
|
2019-02-26 15:41:00 +00:00
|
|
|
" - Start a timer with the given details",
|
|
|
|
" If the first argument looks like a time,",
|
|
|
|
" the timer will be started then (past or future)",
|
2019-02-21 02:44:07 +00:00
|
|
|
},
|
|
|
|
a.opStartTimer,
|
|
|
|
)
|
|
|
|
a.addOperation("stop",
|
|
|
|
[]string{
|
2019-02-26 15:41:00 +00:00
|
|
|
"stop [time] - Stops the current timer",
|
|
|
|
" If the first argument looks like a time,",
|
|
|
|
" the timer will be stopped then (past or future)",
|
2019-02-21 02:44:07 +00:00
|
|
|
},
|
|
|
|
a.opStopTimer,
|
|
|
|
)
|
2019-02-26 15:41:00 +00:00
|
|
|
a.addOperation("switch",
|
|
|
|
[]string{
|
2019-05-01 14:11:06 +00:00
|
|
|
"switch [time] [@contexts...] [+projects...] [tag:value...]",
|
|
|
|
" - Stops all active timers and starts a new one",
|
|
|
|
" with the given arguments",
|
2019-02-26 15:41:00 +00:00
|
|
|
},
|
|
|
|
a.opSwitchTimer,
|
|
|
|
)
|
2019-03-06 14:09:38 +00:00
|
|
|
a.addOperation("rm",
|
|
|
|
[]string{
|
|
|
|
"rm [id] - Removes the timer with the given id",
|
|
|
|
" WARNING: CANNOT BE UNDONE",
|
|
|
|
},
|
|
|
|
a.opRemoveTimer,
|
|
|
|
)
|
2019-02-26 15:41:00 +00:00
|
|
|
a.addOperation("archive",
|
|
|
|
[]string{
|
2019-05-01 13:57:22 +00:00
|
|
|
"archive [id] [@context] [+project] - Archive the timer with the given id,context,project",
|
2019-02-26 15:41:00 +00:00
|
|
|
},
|
|
|
|
a.opArchiveTimer,
|
|
|
|
)
|
2019-02-21 02:44:07 +00:00
|
|
|
a.addOperation("status",
|
|
|
|
[]string{
|
2019-02-26 15:41:00 +00:00
|
|
|
"status - Prints the status of all active timers",
|
2019-02-21 02:44:07 +00:00
|
|
|
},
|
|
|
|
a.opStatus,
|
|
|
|
)
|
2019-02-26 15:41:00 +00:00
|
|
|
a.addOperation("mod",
|
|
|
|
[]string{
|
|
|
|
"mod <id> [start=<start>] [end=<end>] [projects=<project,...>] [contexts=<context,...>]",
|
2019-05-01 14:11:06 +00:00
|
|
|
" - Modify timer with id <id> changes are absolute, so, e.g., if you pass a",
|
|
|
|
" 'projects' tag, the projects on the timer will be replaced with the value",
|
2019-02-26 15:41:00 +00:00
|
|
|
},
|
|
|
|
a.opModifyTimer,
|
|
|
|
)
|
2019-02-21 02:44:07 +00:00
|
|
|
a.addOperation("fuzzyparse",
|
|
|
|
[]string{
|
2019-02-26 15:41:00 +00:00
|
|
|
"fuzzyparse [date string] - Parses the passed string and print the RFC3339 result (for testing)",
|
2019-02-21 02:44:07 +00:00
|
|
|
},
|
|
|
|
a.opFuzzyParse,
|
|
|
|
)
|
|
|
|
a.addOperation("--reinit",
|
2019-05-01 14:11:06 +00:00
|
|
|
[]string{
|
|
|
|
"--reinit - Reset all Configuration Settings",
|
|
|
|
},
|
2019-02-21 02:44:07 +00:00
|
|
|
func(args []string) int {
|
|
|
|
a.initializeConfig()
|
|
|
|
return 0
|
|
|
|
},
|
|
|
|
)
|
|
|
|
a.addOperation("-h",
|
2019-05-01 14:11:06 +00:00
|
|
|
[]string{
|
|
|
|
"-h - Print this message",
|
|
|
|
},
|
2019-02-21 02:44:07 +00:00
|
|
|
a.opPrintUsage,
|
|
|
|
)
|
|
|
|
a.addOperation("help",
|
2019-05-01 14:11:06 +00:00
|
|
|
[]string{
|
|
|
|
"help - Print this message",
|
|
|
|
},
|
2019-02-21 02:44:07 +00:00
|
|
|
a.opPrintUsage,
|
|
|
|
)
|
2019-06-06 19:18:49 +00:00
|
|
|
a.addOperation("config",
|
|
|
|
[]string{
|
|
|
|
"config - Print or edit config",
|
|
|
|
},
|
|
|
|
a.opConfig,
|
|
|
|
)
|
2019-02-21 02:44:07 +00:00
|
|
|
a.addOperation("--h",
|
2019-05-01 14:11:06 +00:00
|
|
|
[]string{
|
|
|
|
"--h - Print this message",
|
|
|
|
},
|
2019-02-21 02:44:07 +00:00
|
|
|
a.opPrintUsage,
|
|
|
|
)
|
2020-05-29 22:24:05 +00:00
|
|
|
a.addOperation("editor",
|
|
|
|
[]string{
|
|
|
|
"editor - Open the timer file in $EDITOR",
|
|
|
|
},
|
|
|
|
a.opEditor,
|
|
|
|
)
|
2019-02-21 02:44:07 +00:00
|
|
|
a.directory = a.config.Get("directory")
|
|
|
|
a.fileTimer = a.config.Get("timerfile")
|
|
|
|
a.fileDone = a.config.Get("donefile")
|
|
|
|
a.fileReport = a.config.Get("reportfile")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppState) initializeConfig() {
|
|
|
|
fmt.Println("Initializing " + a.Name)
|
|
|
|
for {
|
|
|
|
var add string
|
|
|
|
if a.directory != "" {
|
|
|
|
add = " (" + a.directory + ")"
|
|
|
|
}
|
|
|
|
fmt.Println("Path to timer.txt" + add + ":")
|
|
|
|
var resp string
|
|
|
|
fmt.Scanln(&resp)
|
|
|
|
if resp == "" && a.directory != "" {
|
|
|
|
resp = a.directory
|
|
|
|
}
|
|
|
|
if resp != "" {
|
|
|
|
if !strings.HasSuffix(resp, "/") {
|
|
|
|
resp = resp + "/"
|
|
|
|
}
|
|
|
|
fmt.Println("Setting timer.txt directory to: " + resp)
|
|
|
|
a.config.Set("directory", resp)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
a.config.Set("timerfile", "timer.txt")
|
|
|
|
a.config.Set("donefile", "done.txt")
|
|
|
|
a.config.Set("reportfile", "report.txt")
|
|
|
|
}
|