From fb0c6d5f29dcba56a306ecedd8cd6d2f21f32e1e Mon Sep 17 00:00:00 2001 From: Guilherme Henrique Date: Sun, 15 Jan 2023 20:49:43 +0100 Subject: [PATCH] Add history --- store/history.go | 100 ++++++++++++++++++++++++++++++++++++++++++ store/history_test.go | 43 ++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 store/history_test.go diff --git a/store/history.go b/store/history.go index 72440ea..5cedfe0 100644 --- a/store/history.go +++ b/store/history.go @@ -1 +1,101 @@ package store + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strconv" +) + +type HistoryStore struct { + Dir string +} + +func formatEntry(entryKey []byte, n int) []byte { + e := append(entryKey, "\t"...) + return append(e, strconv.Itoa(n)...) + +} + +func (h *HistoryStore) fileName(modeKey string) string { + return filepath.Join(h.Dir, fmt.Sprintf("%s_history", modeKey)) +} + +func (h *HistoryStore) ListEntries(modeKey string) ([][]byte, error) { + fileContent, err := ioutil.ReadFile(h.fileName(modeKey)) + fields := bytes.Split(fileContent, []byte("\n")) + s := make([][]byte, 0) + + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return s, nil + } else { + return nil, err + } + } + + for _, field := range fields { + arr := bytes.Split(field, []byte("\t")) + first := arr[0] + if len(first) > 0 { + s = append(s, first) + } + } + + return s, nil +} + +func (h *HistoryStore) IncrementEntry(modeKey string, entryKey []byte) error { + fileContent, err := ioutil.ReadFile(h.fileName(modeKey)) + + if err != nil { + if errors.Is(err, os.ErrNotExist) { + fileContent = make([]byte, 0) + } else { + return err + } + } + + fields := bytes.Split(fileContent, []byte("\n")) + var found bool + + for i, field := range fields { + arr := bytes.Split(field, []byte("\t")) + first := arr[0] + if bytes.Compare(first, []byte(entryKey)) == 0 { + found = true + + last := arr[len(arr)-1] + n, err := strconv.Atoi(string(last)) + + if err != nil { + n = 0 + } + + fields[i] = formatEntry(entryKey, n+1) + } + } + + if !found { + entry := formatEntry(entryKey, 1) + fields = append(fields, entry) + } + + sort.SliceStable(fields, func(i, j int) bool { + arr1 := bytes.Split(fields[i], []byte("\t")) + a, _ := strconv.Atoi(string(arr1[len(arr1)-1])) + + arr2 := bytes.Split(fields[j], []byte("\t")) + b, _ := strconv.Atoi(string(arr2[len(arr2)-1])) + + return a > b + }) + + finalFields := bytes.Join(fields, []byte("\n")) + + return ioutil.WriteFile(h.fileName(modeKey), finalFields, 0644) +} diff --git a/store/history_test.go b/store/history_test.go new file mode 100644 index 0000000..c4a2508 --- /dev/null +++ b/store/history_test.go @@ -0,0 +1,43 @@ +package store + +import ( + "testing" + + "github.com/gjhenrique/yafl/internal/test" + "github.com/stretchr/testify/require" +) + +func TestOrderIsMaintained(t *testing.T) { + workspace := test.SetupWorkspace(t) + defer workspace.RemoveWorkspace() + + store := HistoryStore{Dir: workspace.CacheDir} + + err := store.IncrementEntry("key", []byte("a")) + require.NoError(t, err) + err = store.IncrementEntry("key", []byte("b")) + require.NoError(t, err) + err = store.IncrementEntry("key", []byte("c")) + require.NoError(t, err) + err = store.IncrementEntry("key", []byte("b")) + require.NoError(t, err) + + entries, err := store.ListEntries("key") + require.NoError(t, err) + + require.Len(t, entries, 3) + require.Equal(t, string(entries[0]), "b") + require.Equal(t, string(entries[1]), "a") + require.Equal(t, string(entries[2]), "c") +} + +func TestEmptyArrayWhenFileDoesNotExist(t *testing.T) { + workspace := test.SetupWorkspace(t) + defer workspace.RemoveWorkspace() + + store := HistoryStore{Dir: workspace.CacheDir} + + entries, err := store.ListEntries("key") + require.NoError(t, err) + require.Len(t, entries, 0) +}