diff --git a/sort.go b/sort.go index c483964..612de2c 100644 --- a/sort.go +++ b/sort.go @@ -13,6 +13,7 @@ const ( SORT_START_DATE_DESC SORT_FINISH_DATE_ASC SORT_FINISH_DATE_DESC + SORT_ERROR ) // Sort allows a TimerList to be sorted by certain predefined fields. @@ -28,8 +29,15 @@ func (timerlist *TimerList) Sort(sortFlag int) error { default: return errors.New("Unrecognized sort option") } + timerlist.sortFlag = sortFlag return nil } +func (timerlist *TimerList) refresh() { + timerlist.Sort(timerlist.sortFlag) + for i, t := range timerlist.timers { + t.Id = i + } +} type timerlistSort struct { timerlists TimerList @@ -37,15 +45,15 @@ type timerlistSort struct { } func (ts *timerlistSort) Len() int { - return len(ts.timerlists) + return len(ts.timerlists.timers) } func (ts *timerlistSort) Swap(l, r int) { - ts.timerlists[l], ts.timerlists[r] = ts.timerlists[r], ts.timerlists[l] + ts.timerlists.timers[l], ts.timerlists.timers[r] = ts.timerlists.timers[r], ts.timerlists.timers[l] } func (ts *timerlistSort) Less(l, r int) bool { - return ts.by(ts.timerlists[l], ts.timerlists[r]) + return ts.by(ts.timerlists.timers[l], ts.timerlists.timers[r]) } func (timerlist *TimerList) sortBy(by func(t1, t2 *Timer) bool) *TimerList { diff --git a/timerlist.go b/timerlist.go index f4eb09c..ac7c55f 100644 --- a/timerlist.go +++ b/timerlist.go @@ -6,32 +6,27 @@ import ( "fmt" "io/ioutil" "os" + "sort" "strings" "time" ) // TimerList represents a list of timer.txt timer entries. // It is usually loasded from a whole timer.txt file. -type TimerList []*Timer +type TimerList struct { + timers []*Timer + sortFlag int +} // NewTimerList creates a new empty TimerList. -func NewTimerList() *TimerList { - return &TimerList{} -} - -func (timerlist *TimerList) Size() int { - return len([]*Timer(*timerlist)) -} - -func (timerlist *TimerList) GetTimerSlice() []*Timer { - return []*Timer(*timerlist) -} +func NewTimerList() *TimerList { return &TimerList{} } +func (timerlist *TimerList) Size() int { return len(timerlist.timers) } +func (timerlist *TimerList) GetTimerSlice() []*Timer { return timerlist.timers } func (timerlist *TimerList) GetMostRecentTimer() (*Timer, error) { var found *Timer var latest time.Time - for i := range *timerlist { - t := ([]*Timer(*timerlist))[i] + for _, t := range timerlist.timers { if t.FinishDate.IsZero() { if t.StartDate.After(latest) { found = t @@ -75,11 +70,81 @@ func (timerlist *TimerList) GetTimersWithProject(project string) *TimerList { }) } +func (timerlist *TimerList) GetContexts() []string { + var ret []string + added := make(map[string]bool) + for _, tmr := range timerlist.timers { + for _, c := range tmr.Contexts { + if !added[c] { + ret = append(ret, c) + added[c] = true + } + } + } + sort.Strings(ret) + return ret +} +func (timerlist *TimerList) GetProjects() []string { + var ret []string + added := make(map[string]bool) + for _, tmr := range timerlist.timers { + for _, p := range tmr.Projects { + if !added[p] { + ret = append(ret, p) + added[p] = true + } + } + } + sort.Strings(ret) + return ret +} +func (timerlist *TimerList) GetTagKVList() []string { + var ret []string + added := make(map[string]bool) + for _, tmr := range timerlist.timers { + for k, v := range tmr.AdditionalTags { + tag := fmt.Sprintf("%s:%s", k, v) + if !added[tag] { + ret = append(ret, tag) + added[tag] = true + } + } + } + sort.Strings(ret) + return ret +} +func (timerlist *TimerList) GetTagKeys() []string { + var ret []string + added := make(map[string]bool) + for _, tmr := range timerlist.timers { + for k := range tmr.AdditionalTags { + if !added[k] { + ret = append(ret, k) + added[k] = true + } + } + } + sort.Strings(ret) + return ret +} +func (timerlist *TimerList) GetTagValuesForKey(key string) []string { + var ret []string + added := make(map[string]bool) + for _, tmr := range timerlist.timers { + if v, ok := tmr.AdditionalTags[key]; ok && !added[v] { + ret = append(ret, v) + added[v] = true + } + } + sort.Strings(ret) + return ret +} + func (timerlist *TimerList) GetActiveTimers() *TimerList { t := *NewTimerList() - for _, v := range *timerlist { + for _, v := range timerlist.timers { if v.FinishDate.IsZero() { - t = append(t, v) + t.timers = append(t.timers, v) } } return &t @@ -88,7 +153,7 @@ func (timerlist *TimerList) GetActiveTimers() *TimerList { // String returns a complete list of timers in timer.txt format. func (timerlist *TimerList) String() string { var ret string - for _, timer := range *timerlist { + for _, timer := range timerlist.timers { ret += fmt.Sprintf("%s\n", timer.String()) } return ret @@ -96,24 +161,23 @@ func (timerlist *TimerList) String() string { // AddTimer prepends a Timer to the current TimerList and takes care to set the Timer.Id correctly func (timerlist *TimerList) AddTimer(timer *Timer) { - // The new timer is going to be id 1 - timer.Id = 1 - for _, t := range *timerlist { - // Everything else gets incremented - t.Id++ - } - // Now prepend the timer to the slice - *timerlist = append(*timerlist, &Timer{}) - copy((*timerlist)[1:], (*timerlist)[0:]) - (*timerlist)[0] = timer + timerlist.timers = append(timerlist.timers, timer) + timerlist.refresh() } +// AddTimers adds all passed in timers to the list, sorts the list, then updates the Timer.Id values. +func (timerlist *TimerList) AddTimers(timers []*Timer) { + timerlist.timers = append(timerlist.timers, timers...) + timerlist.Sort(SORT_START_DATE_ASC) +} +func (timerlist *TimerList) Combine(other *TimerList) { timerlist.AddTimers(other.timers) } + // GetTimer returns the Timer with the given timer 'id' from the TimerList. // Returns an error if Timer could not be found. func (timerlist *TimerList) GetTimer(id int) (*Timer, error) { - for i := range *timerlist { - if ([]*Timer(*timerlist))[i].Id == id { - return ([]*Timer(*timerlist))[i], nil + for i := range timerlist.timers { + if timerlist.timers[i].Id == id { + return timerlist.timers[i], nil } } return nil, errors.New("timer not found") @@ -122,38 +186,40 @@ func (timerlist *TimerList) GetTimer(id int) (*Timer, error) { // RemoveTimerById removes any Timer with given Timer 'id' from the TimerList. // Returns an error if no Timer was removed. func (timerlist *TimerList) RemoveTimerById(id int) error { - var newList TimerList found := false - for _, t := range *timerlist { - if t.Id != id { - newList = append(newList, t) - } else { + var remIdx int + var t *Timer + for remIdx, t = range timerlist.timers { + if t.Id == id { found = true + break } } if !found { return errors.New("timer not found") } - *timerlist = newList + timerlist.timers = append(timerlist.timers[:remIdx], timerlist.timers[remIdx+1:]...) + timerlist.refresh() return nil } // RemoveTimer removes any Timer from the TimerList with the same String representation as the given Timer. // Returns an error if no Timer was removed. func (timerlist *TimerList) RemoveTimer(timer Timer) error { - var newList TimerList found := false - for _, t := range *timerlist { - if t.String() != timer.String() { - newList = append(newList, t) - } else { + var remIdx int + var t *Timer + for remIdx, t = range timerlist.timers { + if t.String() == timer.String() { found = true + break } } if !found { return errors.New("timer not found") } - *timerlist = newList + timerlist.timers = append(timerlist.timers[:remIdx], timerlist.timers[remIdx+1:]...) + timerlist.refresh() return nil } @@ -177,9 +243,9 @@ func (timerlist *TimerList) ArchiveTimerToFile(timer Timer, filename string) err // bool), and returns a new TimerList. The original TimerList is not modified. func (timerlist *TimerList) Filter(predicate func(*Timer) bool) *TimerList { var newList TimerList - for _, t := range *timerlist { + for _, t := range timerlist.timers { if predicate(t) { - newList = append(newList, t) + newList.timers = append(newList.timers, t) } } return &newList @@ -188,7 +254,7 @@ func (timerlist *TimerList) Filter(predicate func(*Timer) bool) *TimerList { // LoadFromFile loads a TimerList from *os.File. // Note: This will clear the current TimerList and overwrite it's contents with whatever is in *os.File. func (timerlist *TimerList) LoadFromFile(file *os.File) error { - *timerlist = []*Timer{} // Empty timerlist + timerlist.timers = []*Timer{} // Empty timerlist timerId := 1 scanner := bufio.NewScanner(file) for scanner.Scan() { @@ -202,12 +268,13 @@ func (timerlist *TimerList) LoadFromFile(file *os.File) error { return err } timer.Id = timerId - *timerlist = append(*timerlist, timer) timerId++ + timerlist.timers = append(timerlist.timers, timer) } if err := scanner.Err(); err != nil { return err } + timerlist.refresh() return nil } @@ -235,12 +302,12 @@ func (timerlist *TimerList) WriteToFilename(filename string) error { } // LoadFromFile loads and returns a TimerList from *os.File. -func LoadFromFile(file *os.File) (TimerList, error) { +func LoadFromFile(file *os.File) (*TimerList, error) { timerlist := TimerList{} if err := timerlist.LoadFromFile(file); err != nil { return nil, err } - return timerlist, nil + return &timerlist, nil } // WriteToFile writes a TimerList to *os.File. @@ -249,12 +316,12 @@ func WriteToFile(timerlist *TimerList, file *os.File) error { } // LoadFromFilename loads and returns a TimerList from a file (most likely called "timer.txt") -func LoadFromFilename(filename string) (TimerList, error) { +func LoadFromFilename(filename string) (*TimerList, error) { timerlist := TimerList{} if err := timerlist.LoadFromFilename(filename); err != nil { return nil, err } - return timerlist, nil + return &timerlist, nil } // WriteToFilename write a TimerList to the specified file (most likely called "timer.txt")