aboutsummaryrefslogblamecommitdiff
path: root/internal/tracker/tracker.go
blob: 6d9f058a5323581cd85a1a99b5fa803f0d0a737a (plain) (tree)



































































































































                                                                                                                    
package tracker

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/spf13/afero"
)

var (
	ErrEntryAlreadyExists = fmt.Errorf("entry with this title already exists")
	ErrActivityNotFound   = fmt.Errorf("there is no activity with given id")
)

type Tracker struct {
	fs       afero.Fs
	document *Document
}

const FileName = "gotrack.json"

func New(fs afero.Fs) (*Tracker, error) {
	t := &Tracker{fs: fs}
	if err := t.load(); err != nil {
		return nil, err
	}
	return t, nil
}

func (t *Tracker) Add(title string, tags []string, contexts []string) (int, error) {
	for _, activity := range t.document.Activities {
		if strings.ToLower(strings.Trim(activity.Title, " ")) == strings.ToLower(strings.Trim(title, " ")) {
			return 0, ErrEntryAlreadyExists
		}
	}
	activity := Activity{
		ID:      t.document.LastKey + 1,
		Title:   title,
		Tags:    tags,
		Context: contexts,
		Spans:   []*Span{},
	}

	t.document = &Document{
		LastKey:    activity.ID,
		Activities: append(t.document.Activities, &activity),
	}

	return t.document.LastKey, t.save()
}

func (t *Tracker) Start(id int, comment string) error {
	if err := t.Stop(id); err != nil {
		return err
	}
	span := &Span{
		Start:   time.Now(),
		Comment: comment,
	}
	activity := t.Activity(id)
	activity.Spans = append(activity.Spans, span)
	return t.save()
}

func (t *Tracker) Stop(id int) error {
	activity := t.Activity(id)
	if activity == nil {
		return ErrActivityNotFound
	}
	if span := activity.Started(); span != nil {
		t := time.Now()
		span.Stop = &t
	}
	return t.save()
}

func (t *Tracker) List(all bool) []*Activity {
	if all {
		return t.document.Activities
	}
	return filterActivities(t.document.Activities, func(a *Activity) bool {
		return a.Started() != nil
	})
}

func (t *Tracker) Activity(id int) *Activity {
	for _, activity := range t.document.Activities {
		if activity.ID == id {
			return activity
		}
	}
	return nil
}

func (t *Tracker) load() (err error) {
	t.document = new(Document)
	f, err := t.fs.Open(FileName)
	defer func() {
		err = f.Close()
	}()
	if err != nil {
		f, err = t.fs.Create(FileName)
		if err != nil {
			return err
		}
		return nil
	}
	return json.NewDecoder(f).Decode(t.document)
}

func (t *Tracker) save() (err error) {
	f, err := t.fs.Create(FileName)
	if err != nil {
		return err
	}
	defer func() {
		err = f.Close()
	}()
	return json.NewEncoder(f).Encode(t.document)
}

func filterActivities(list []*Activity, filter func(activity *Activity) bool) []*Activity {
	var filtered []*Activity
	for _, activity := range list {
		if filter(activity) {
			filtered = append(filtered, activity)
		}
	}
	return filtered
}