commit 4d8db75d7019b916b1b2c2cac917fbcb97f925b1
parent d30f0b07dd9a6863da2dbcefecfc28e949b0b140
Author: Tomas Nemec <nemi@skaut.cz>
Date: Tue, 21 Feb 2023 16:07:47 +0100
update
Diffstat:
7 files changed, 148 insertions(+), 159 deletions(-)
diff --git a/command.go b/command.go
@@ -53,9 +53,9 @@ func (c Command) add() {
group := NewGroup(groupArg)
- entry, err := NewCompletedEntry(start, stop)
+ entry, err := NewCompleted(start, stop)
if err != nil {
- fmt.Fprintln(os.Stderr, err)
+ fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
@@ -86,7 +86,7 @@ func (c Command) start() {
}
}
- entry := NewRunningEntry(start)
+ entry := NewRunning(start)
if c.repository.ExistsEntry(group, entry) {
fmt.Fprintln(os.Stderr, "entry in this group already started")
@@ -115,24 +115,20 @@ func (c Command) stop() {
}
}
- runningEntry, err := c.repository.RunningEntry(group, c.entryTimeContext)
+ entry, err := c.repository.RunningEntry(group, c.entryTimeContext)
if err != nil {
fmt.Fprintf(os.Stderr, "no entry running in %q\n", groupArg)
os.Exit(1)
}
- entry, err := runningEntry.Complete(stop)
- if err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
+ entry.Complete(stop)
if c.repository.ExistsEntry(group, entry) {
fmt.Fprintln(os.Stderr, "entry already created")
os.Exit(1)
}
- c.repository.Remove(group, runningEntry)
+ c.repository.Remove(group, entry)
c.repository.Save(group, entry)
}
@@ -211,15 +207,19 @@ func (c Command) report() {
var atLeastOneEntry bool
var totalDuration time.Duration
for _, group := range groups {
- entries, err := c.repository.ListCompleted(group, c.entryTimeContext)
+ entries, err := c.repository.List(group, c.entryTimeContext)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
for _, entry := range entries {
- if (entry.Start.After(sinceTime) || entry.Start.Equal(sinceTime)) &&
- (entry.Stop.Before(untilTime) || entry.Stop.Equal(untilTime)) {
+ if !entry.Completed {
+ continue
+ }
+
+ if (entry.TimeRange.Start.After(sinceTime) || entry.TimeRange.Start.Equal(sinceTime)) &&
+ (entry.TimeRange.Stop.Before(untilTime) || entry.TimeRange.Stop.Equal(untilTime)) {
atLeastOneEntry = true
totalDuration += entry.Duration()
formatEntry(group, entry)
@@ -250,15 +250,14 @@ func formatEntry(group Group, entry Entry) {
var start, stop string
var duration time.Duration
- switch e := entry.(type) {
- case CompletedEntry:
- start = e.Start.Format(timeLayout)
- stop = e.Stop.Format(timeLayout)
- duration = e.Duration().Round(time.Second)
- case RunningEntry:
- start = e.Start.Format(timeLayout)
- duration = time.Since(e.Start).Round(time.Second)
+ start = entry.TimeRange.Start.Format(timeLayout)
+ if entry.Completed {
+ stop = entry.TimeRange.Stop.Format(timeLayout)
+ duration = entry.Duration().Round(time.Second)
+ } else {
+ start = entry.TimeRange.Start.Format(timeLayout)
stop = "running"
+ duration = time.Since(entry.TimeRange.Start).Round(time.Second)
}
fmt.Printf("%s\t%s\t%v\t%s\n", groupPath, start, stop, duration)
diff --git a/entry.go b/entry.go
@@ -1,46 +1,34 @@
package main
-import (
- "errors"
- "time"
-)
+import "time"
-type Entry interface{}
-
-type CompletedEntry struct {
- Start time.Time
- Stop time.Time
+type Entry struct {
+ Completed bool
+ TimeRange TimeRange
}
-func NewCompletedEntry(start time.Time, stop time.Time) (CompletedEntry, error) {
- if start.After(stop) || start.Equal(stop) {
- return CompletedEntry{}, errors.New("duration must be positive")
+func NewCompleted(start time.Time, stop time.Time) (Entry, error) {
+ timeRange, err := New(start, stop)
+ if err != nil {
+ return Entry{}, err
}
- return CompletedEntry{
- Start: start,
- Stop: stop,
- }, nil
+ return Entry{TimeRange: timeRange, Completed: true}, nil
}
-func (e CompletedEntry) Duration() time.Duration {
- return e.Stop.Sub(e.Start)
+func NewRunning(start time.Time) Entry {
+ timeRange, _ := New(start, start.Add(time.Second))
+ return Entry{TimeRange: timeRange, Completed: false}
}
-type RunningEntry struct {
- Start time.Time
+func (e Entry) Duration() time.Duration {
+ return e.TimeRange.Stop.Sub(e.TimeRange.Start)
}
-func NewRunningEntry(start time.Time) RunningEntry {
- return RunningEntry{
- Start: start,
+func (e *Entry) Complete(stop time.Time) {
+ e.TimeRange = TimeRange{
+ Start: e.TimeRange.Start,
+ Stop: stop,
}
-}
-
-func (e RunningEntry) Duration() time.Duration {
- return time.Since(e.Start)
-}
-
-func (e RunningEntry) Complete(stop time.Time) (CompletedEntry, error) {
- return NewCompletedEntry(e.Start, stop)
+ e.Completed = true
}
diff --git a/entry_test.go b/entry_test.go
@@ -1,8 +1,6 @@
package main
-import (
- "testing"
-)
+import "testing"
func TestEntry(t *testing.T) {
entryTime := NewTimeToday()
@@ -10,7 +8,7 @@ func TestEntry(t *testing.T) {
t.Run("positive duration should pass", func(b *testing.T) {
start, _ := entryTime.ParseArg("5:00")
stop, _ := entryTime.ParseArg("6:00")
- _, err := NewCompletedEntry(start, stop)
+ _, err := NewCompleted(start, stop)
if err != nil {
b.Error(err)
}
@@ -19,7 +17,7 @@ func TestEntry(t *testing.T) {
t.Run("zero duration should fail", func(b *testing.T) {
start, _ := entryTime.ParseArg("6:00")
stop, _ := entryTime.ParseArg("6:00")
- _, err := NewCompletedEntry(start, stop)
+ _, err := NewCompleted(start, stop)
if err == nil {
b.Error(err)
}
@@ -28,7 +26,7 @@ func TestEntry(t *testing.T) {
t.Run("negative duration should fail", func(b *testing.T) {
start, _ := entryTime.ParseArg("6:00")
stop, _ := entryTime.ParseArg("5:00")
- _, err := NewCompletedEntry(start, stop)
+ _, err := NewCompleted(start, stop)
if err == nil {
b.Error(err)
}
diff --git a/range.go b/range.go
@@ -1,12 +1,19 @@
package main
-import "time"
+import (
+ "errors"
+ "time"
+)
-type Range interface {
- Entries() []Entry
+type TimeRange struct {
+ Start time.Time
+ Stop time.Time
}
-type TimeRange struct {
- since time.Time
- until time.Time
+func New(start time.Time, stop time.Time) (TimeRange, error) {
+ if start.After(stop) || start.Equal(stop) {
+ return TimeRange{}, errors.New("duration must be positive")
+ }
+
+ return TimeRange{Start: start, Stop: stop}, nil
}
diff --git a/repository.go b/repository.go
@@ -8,6 +8,7 @@ import (
"path"
"path/filepath"
"strings"
+ "time"
)
type FSRepository struct {
@@ -18,76 +19,24 @@ func NewFSRepository(rootPath string) FSRepository {
return FSRepository{rootPath}
}
-func (repo FSRepository) NewEntry(entryPath string, entryTime *TimeContext) (Entry, error) {
- if _, err := os.Stat(entryPath); os.IsNotExist(err) {
- return CompletedEntry{}, err
- }
-
- data, err := os.ReadFile(entryPath)
- if err != nil {
- return CompletedEntry{}, err
- }
-
- // TODO(tms) 11.11.22: format check
-
- base := path.Base(entryPath)
- lines := strings.Split(string(data), "\n")
-
- if base == activeFile {
- start, _ := entryTime.ParseEntry(lines[0])
- return RunningEntry{
- Start: start,
- }, nil
- } else {
- start, _ := entryTime.ParseEntry(lines[0])
- stop, _ := entryTime.ParseEntry(lines[1])
- return CompletedEntry{
- Start: start,
- Stop: stop,
- }, nil
- }
-}
-
-func (repo FSRepository) CompletedEntry(entryPath string, entryTime *TimeContext) (CompletedEntry, error) {
- if _, err := os.Stat(entryPath); os.IsNotExist(err) {
- return CompletedEntry{}, err
- }
-
- data, err := os.ReadFile(entryPath)
- if err != nil {
- return CompletedEntry{}, err
- }
-
- // TODO(tms) 11.11.22: format check
-
- lines := strings.Split(string(data), "\n")
- start, _ := entryTime.ParseEntry(lines[0])
- stop, _ := entryTime.ParseEntry(lines[1])
-
- return CompletedEntry{
- Start: start,
- Stop: stop,
- }, nil
-}
-
-func (repo FSRepository) RunningEntry(group Group, entryTime *TimeContext) (RunningEntry, error) {
+func (repo FSRepository) RunningEntry(group Group, entryTime *TimeContext) (Entry, error) {
entryPath := path.Join(repo.rootPath, group.Name, activeFile)
if _, err := os.Stat(entryPath); os.IsNotExist(err) {
- return RunningEntry{}, err
+ return Entry{}, err
}
data, err := os.ReadFile(entryPath)
if err != nil {
- return RunningEntry{}, err
+ return Entry{}, err
}
firstLine := strings.Split(string(data), "\n")[0]
start, _ := entryTime.ParseEntry(firstLine)
- return RunningEntry{
- Start: start,
+ return Entry{
+ TimeRange: TimeRange{start, time.Now()},
+ Completed: false,
}, nil
-
}
func (r FSRepository) Save(group Group, entry Entry) error {
@@ -106,25 +55,19 @@ func (r FSRepository) Save(group Group, entry Entry) error {
}
func (repo FSRepository) EntryData(entry Entry) string {
- switch e := entry.(type) {
- case CompletedEntry:
- return fmt.Sprintf("%s\n%s\n", e.Start.Format(DataTimeLayout), e.Stop.Format(DataTimeLayout))
- case RunningEntry:
- return fmt.Sprintf("%s\n", e.Start.Format(DataTimeLayout))
+ if entry.Completed {
+ return fmt.Sprintf("%s\n%s\n", entry.TimeRange.Start.Format(DataTimeLayout), entry.TimeRange.Stop.Format(DataTimeLayout))
}
- return "_UNKNOWNTYPE_"
+ return fmt.Sprintf("%s\n", entry.TimeRange.Start.Format(DataTimeLayout))
}
func (r FSRepository) EntryName(entry Entry) string {
- switch e := entry.(type) {
- case CompletedEntry:
- return strings.Join([]string{e.Start.Format(FileNameLayout), e.Stop.Format(FileNameLayout)}, "_")
- case RunningEntry:
- return activeFile
+ if entry.Completed {
+ return strings.Join([]string{entry.TimeRange.Start.Format(FileNameLayout), entry.TimeRange.Stop.Format(FileNameLayout)}, "_")
}
- return "_UNKNOWNTYPE_"
+ return activeFile
}
func (repo FSRepository) Remove(group Group, entry Entry) error {
@@ -139,6 +82,7 @@ func (repo FSRepository) ExistsEntry(group Group, entry Entry) bool {
}
return false
}
+
func (repo FSRepository) Exists(group Group) bool {
groupPath := path.Join(repo.rootPath, group.Name)
if _, err := os.Stat(groupPath); !os.IsNotExist(err) {
@@ -183,7 +127,7 @@ func (repo FSRepository) List(group Group, entryTime *TimeContext) ([]Entry, err
return fs.SkipDir
}
- entry, err := repo.NewEntry(path, entryTime)
+ entry, err := repo.find(path, entryTime)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
@@ -198,33 +142,29 @@ func (repo FSRepository) List(group Group, entryTime *TimeContext) ([]Entry, err
return entries, nil
}
-func (repo FSRepository) ListCompleted(group Group, entryTime *TimeContext) ([]CompletedEntry, error) {
- entries, err := repo.List(group, entryTime)
- if err != nil {
- return []CompletedEntry{}, err
+func (repo FSRepository) find(entryPath string, entryTime *TimeContext) (Entry, error) {
+ if _, err := os.Stat(entryPath); os.IsNotExist(err) {
+ return Entry{}, err
}
- return listFilter[CompletedEntry](entries)
-}
-
-func (repo FSRepository) ListRunning(group Group, entryTime *TimeContext) ([]RunningEntry, error) {
- entries, err := repo.List(group, entryTime)
+ data, err := os.ReadFile(entryPath)
if err != nil {
- return []RunningEntry{}, err
+ return Entry{}, err
}
- return listFilter[RunningEntry](entries)
-}
+ // TODO(tms) 11.11.22: format check
+ base := path.Base(entryPath)
-func listFilter[K CompletedEntry | RunningEntry](entries []Entry) ([]K, error) {
- var completedEntries []K
+ lines := strings.Split(string(data), "\n")
- for _, entry := range entries {
- switch e := entry.(type) {
- case K:
- completedEntries = append(completedEntries, e)
- }
+ if base == activeFile {
+ start, _ := entryTime.ParseEntry(lines[0])
+ timeRange := TimeRange{start, time.Now()}
+ return Entry{Completed: false, TimeRange: timeRange}, nil
+ } else {
+ start, _ := entryTime.ParseEntry(lines[0])
+ stop, _ := entryTime.ParseEntry(lines[1])
+ timeRange := TimeRange{start, stop}
+ return Entry{Completed: true, TimeRange: timeRange}, nil
}
-
- return completedEntries, nil
}
diff --git a/repository_test.go b/repository_test.go
@@ -0,0 +1,54 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestSave(t *testing.T) {
+ rootPath := setUp()
+ t.Cleanup(func() { tearDown(rootPath) })
+ timeContext := NewTimeToday()
+ repository := NewFSRepository(rootPath)
+
+ t.Run("save single entry", func(t *testing.T) {
+ group := NewGroup("test")
+ entry := createEntry(timeContext, "10:00", "11:00")
+
+ err := repository.Save(group, entry)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if !repository.ExistsEntry(group, entry) {
+ t.Error(err)
+ }
+ })
+
+ t.Run("save single entry", func(t *testing.T) {
+ group := NewGroup("test")
+
+ for _, entry := range []Entry{
+ createEntry(timeContext, "10:00", "11:00"),
+ createEntry(timeContext, "11:00", "12:00"),
+ createEntry(timeContext, "13:00", "14:00"),
+ } {
+ err := repository.Save(group, entry)
+ if err != nil {
+ t.Error(err)
+ }
+ }
+
+ list, _ := repository.List(group, timeContext)
+
+ if len(list) != 3 {
+ t.Error("Expected 3 entries")
+ }
+ })
+}
+
+func createEntry(timeContext *TimeContext, s string, st string) Entry {
+ start, _ := timeContext.ParseArg(s)
+ stop, _ := timeContext.ParseArgRight(st)
+ entry, _ := NewCompleted(start, stop)
+ return entry
+}
diff --git a/tme_test.go b/tme_test.go
@@ -1,6 +1,8 @@
package main
-import "os"
+import (
+ "os"
+)
// func TestAdd(t *testing.T) {
// rootPath := setUp()
@@ -9,8 +11,8 @@ import "os"
// group := createAndCheckGroup(t, rootPath, "project")
// start, _ := entryTime.ParseArg("10:00")
-// stop, _ := entryTime.ParseArg("11:00")
-// tme, err := NewCompletedEntry(start, stop)
+// stop, _ := entryTime.ParseArgRight("11:00")
+// tme, err := NewCompleted(start, stop)
// if err != nil {
// t.Error(err)
// }
@@ -134,6 +136,7 @@ import "os"
// t.Fatalf("time mismatch! %v != %v", start, lineStart)
// }
// }
+
// func createAndCheckGroup(t *testing.T, basePath, groupName string) Group {
// group := NewGroup(basePath, groupName)
// group.Create()