diff --git a/cmd/gime/README.md b/cmd/gime/README.md new file mode 100644 index 0000000..9f465fc --- /dev/null +++ b/cmd/gime/README.md @@ -0,0 +1,4 @@ +gime +==== + +This folder contains the cli utility for gime diff --git a/cmd/gime/gime b/cmd/gime/gime new file mode 100755 index 0000000..e8a8fb8 Binary files /dev/null and b/cmd/gime/gime differ diff --git a/cmd/gime/main.go b/cmd/gime/main.go new file mode 100644 index 0000000..15740bf --- /dev/null +++ b/cmd/gime/main.go @@ -0,0 +1,147 @@ +package main + +import ( + "fmt" + "os" + "time" + + "git.bullercodeworks.com/brian/gime" +) + +const ( + AppName = "gime" + AppVersion = 1 +) + +var validOperations map[string][]string +var activeTimeEntry *gime.TimeEntry + +func main() { + initialize() + var parms []string + if len(os.Args) > 1 { + parms = os.Args[1:] + parms[0] = matchParameter(parms[0]) + } else { + // If no parameters were passed, just print the status + parms = append(parms, "status") + } + + switch parms[0] { + case "help": + printHelp() + case "status": + printStatus() + case "start": + startTimer(parms[1:]) + case "end", "stop": + stopTimer(parms[1:]) + } +} + +func printHelp() { + fmt.Println("gime - A simple timekeeping application\n") + for _, v := range validOperations { + for vi := range v { + fmt.Println(" ", v[vi]) + } + fmt.Println("") + } +} + +func printStatus() { + if activeTimeEntry == nil { + fmt.Println("No timer running") + } else { + fmt.Print("Timer started at ") + curr := time.Now() + if activeTimeEntry.GetStart().Day() == curr.Day() { + fmt.Println(activeTimeEntry.GetStart().Format("15:04")) + } else { + fmt.Println(activeTimeEntry.GetStart().Format(time.Stamp)) + } + fmt.Println(time.Since(activeTimeEntry.GetStart()).String()) + } +} + +func startTimer(args []string) { + var err error + var tm time.Time + st := time.Now() + if len(args) > 0 { + // Check if the first argument looks like a date/time + tm, err = time.Parse("15:04", args[0]) + if err != nil { + tm, err = time.Parse(time.Kitchen, args[0]) + } + } + if err != nil { + // Just start it now + } + _, _ = tm, st +} + +func stopTimer(args []string) { + var err error + var tm time.Time + st := time.Now() + if len(args) > 0 { + // Check if the first argument looks like a date/time + tm, err = time.Parse("15:04", args[0]) + if err != nil { + tm, err = time.Parse(time.Kitchen, args[0]) + } + + } + _, _ = tm, st +} + +func initialize() { + validOperations = make(map[string][]string) + validOperations["status"] = []string{ + "status - Print the current status of the timer", + } + validOperations["start"] = []string{ + "start [time] [tags ...] - Start a timer with the given tags (space separated)", + " If the first sub-argument given looks like a time,", + " the timer will be started then (past or future).", + " If a timer is already running it'll be stopped", + } + validOperations["stop"] = []string{ + "stop [time] - Stops the current timer", + " If the first sub-argument given looks like a time,", + " the timer will be stopped then (past or future).", + } + validOperations["end"] = []string{ + "end - The same as stop", + } + validOperations["help"] = []string{ + "help - Print this", + } +} + +func matchParameter(in string) string { + var chkParms []string + for k := range validOperations { + chkParms = append(chkParms, k) + } + var nextParms []string + for i := range in { + for _, p := range chkParms { + if p[i] == in[i] { + nextParms = append(nextParms, p) + } + } + // If we get here and there is only one parameter left, return it + chkParms = nextParms + if len(nextParms) == 1 { + break + } + // Otherwise, loop + nextParms = []string{} + } + if len(chkParms) == 0 { + return "" + } + return chkParms[0] +} diff --git a/model.go b/model.go new file mode 100644 index 0000000..2178d26 --- /dev/null +++ b/model.go @@ -0,0 +1 @@ +package gime diff --git a/timeentry.go b/timeentry.go new file mode 100644 index 0000000..eb7357a --- /dev/null +++ b/timeentry.go @@ -0,0 +1,101 @@ +package gime + +import ( + "errors" + "time" + + "github.com/pborman/uuid" +) + +// Entry is a time entry +type TimeEntry struct { + uuid string + start time.Time + end time.Time + tags []string +} + +// CreateTimeEntry creates an entry with the given parameters and returns it. +// An error is returned if no start time is given or if the end time given is +// non-zero and earlier than the start time. +func CreateTimeEntry(s, e time.Time, t []string) (*TimeEntry, error) { + var ret *TimeEntry + ret.uuid = uuid.New() + if s.IsZero() { + // No start time given, return error + return ret, errors.New("No start time given") + } + if !e.IsZero() { + if !e.After(s) { + // Given end time is earlier than start time + return ret, errors.New("End time is before start time") + } + } + return &TimeEntry{start: s, end: e, tags: t}, nil +} + +// GetStart returns the start time for the entry +func (t *TimeEntry) GetStart() time.Time { + return t.start +} + +// SetStart sets the start time on the time entry +func (t *TimeEntry) SetStart(s time.Time) { + t.start = s +} + +// GetEnd returns the end time for the entry +func (t *TimeEntry) GetEnd() time.Time { + return t.end +} + +// SetEnd sets the end time on the time entry +func (t *TimeEntry) SetEnd(e time.Time) { + t.end = e +} + +// IsActive return true if start is earlier than now and end is zero +func (t *TimeEntry) IsActive() bool { + return time.Now().After(t.start) && !t.end.IsZero() +} + +// GetTags returns all of the tags associated with this time entry +func (t *TimeEntry) GetTags() []string { + return t.tags +} + +// HasTag returns if the time entry contains a specific tag +func (t *TimeEntry) HasTag(s string) bool { + for i := range t.tags { + if t.tags[i] == s { + return true + } + } + return false +} + +// AddTag adds a tag to the time entry (if it isn't already there) +func (t *TimeEntry) AddTag(s string) { + if !t.HasTag(s) { + t.tags = append(t.tags, s) + } +} + +// RemoveTag removes a tag from the time entry +func (t *TimeEntry) RemoveTag(s string) { + var i int + var fnd bool + for i = range t.tags { + if t.tags[i] == s { + fnd = true + break + } + } + if fnd { + t.tags = append(t.tags[:i], t.tags[i+1:]...) + } +} + +func (t *TimeEntry) Equals(tst *TimeEntry) bool { + return t.uuid == tst.uuid +} diff --git a/timeentry_collection.go b/timeentry_collection.go new file mode 100644 index 0000000..1e42871 --- /dev/null +++ b/timeentry_collection.go @@ -0,0 +1,80 @@ +package gime + +import "fmt" + +// TimeEntryCollection is a collection of Time Entries for gomobile bindings +type TimeEntryCollection struct { + list []TimeEntry +} + +// Length returns how many time entries are in the collection +func (tc *TimeEntryCollection) Length() int { + return len(tc.list) +} + +// Get returns the TimeEntry at idx or a nil entry +func (tc *TimeEntryCollection) Get(idx int) *TimeEntry { + if idx <= tc.Length() { + return &tc.list[idx] + } + return nil +} + +// Clear empties the list of TimeEntries +func (tc *TimeEntryCollection) Clear() { + tc.list = tc.list[:0] +} + +// Index finds the index of the given TimeEntry or -1 if not found +func (tc *TimeEntryCollection) Index(t *TimeEntry) int { + for i, tst := range tc.list { + if tst.Equals(t) { + return i + } + } + return -1 +} + +// Insert inserts a TimeEntry into the collection at i +func (tc *TimeEntryCollection) Insert(i int, t *TimeEntry) { + if i < 0 || i > tc.Length() { + fmt.Println("gime: Attempted to insert time entry at invalid index") + } + tc.list = append(tc.list, TimeEntry{}) + copy(tc.list[i+1:], tc.list[i:]) + tc.list[i] = *t +} + +// Remove removes the TimeEntry at i from the collection +func (tc *TimeEntryCollection) Remove(i int) { + if i < 0 || i >= tc.Length() { + fmt.Println("gime: Attempted to remove time entry at invalid index") + } + copy(tc.list[i:], tc.list[i+1:]) + tc.list[len(tc.list)-1] = TimeEntry{} + tc.list = tc.list[:len(tc.list)-1] +} + +// Push adds an element to the end of the collection +func (tc *TimeEntryCollection) Push(t *TimeEntry) { + tc.Insert(tc.Length(), t) +} + +// Pop removes the last element from the collection +func (tc *TimeEntryCollection) Pop() *TimeEntry { + ret := tc.list[tc.Length()-1] + tc.Remove(tc.Length() - 1) + return &ret +} + +// Unshift adds an element to the front of the collection +func (tc *TimeEntryCollection) Unshift(t *TimeEntry) { + tc.Insert(0, t) +} + +// Shift removes an element from the front of the collection +func (tc *TimeEntryCollection) Shift(t *TimeEntry) *TimeEntry { + ret := tc.list[0] + tc.Remove(0) + return &ret +}