From ce5bf71f6b13f5cf38de1c3f0d3e8363e1716d18 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 29 Oct 2023 09:05:39 -0600 Subject: [PATCH] wip --- src/ledger/file.go | 37 +++++++++++++++++++ src/ledger/file_test.go | 82 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/src/ledger/file.go b/src/ledger/file.go index 30ecb24..57abd1a 100644 --- a/src/ledger/file.go +++ b/src/ledger/file.go @@ -1,6 +1,8 @@ package ledger import ( + "bufio" + "bytes" "fmt" "io" "io/fs" @@ -20,6 +22,41 @@ func NewFiles(p string, q ...string) (Files, error) { return f, err } +func (files Files) TempGetLastNLines(n int) ([]string, error) { + p := files.paths()[0] + f, err := os.Open(p) + if err != nil { + return nil, err + } + defer f.Close() + return peekLastNLines(io.Discard, bufio.NewReader(f), n) +} + +func (files Files) TempSetLastNLines(n int, lines []string) error { + panic("e") +} + +func peekLastNLines(w io.Writer, r *bufio.Reader, n int) ([]string, error) { + lastNLines := make([]string, 0, n) + for { + line, err := r.ReadBytes('\n') + if len(line) > 0 { + lastNLines = append(lastNLines, string(bytes.TrimRight(line, "\n"))) + for i := 0; i < len(lastNLines)-n; i++ { + fmt.Fprintln(w, lastNLines[i]) + } + lastNLines = lastNLines[max(0, len(lastNLines)-n):] + } + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + } + return lastNLines, nil +} + func (files Files) paths() []string { result := make([]string, 0, len(files)) for i := range files { diff --git a/src/ledger/file_test.go b/src/ledger/file_test.go index fb93259..9b8b356 100644 --- a/src/ledger/file_test.go +++ b/src/ledger/file_test.go @@ -1,6 +1,8 @@ package ledger import ( + "encoding/base64" + "fmt" "os" "path" "path/filepath" @@ -290,3 +292,83 @@ func TestFilesOfDir(t *testing.T) { t.Error(paths) } } + +func TestFilesTempGetLastNLines(t *testing.T) { + cases := map[string]struct { + input string + n int + want []string + }{ + "empty": {}, + "get n lines from empty file": { + input: "", + n: 5, + want: []string{}, + }, + "get 0 lines from file": { + input: "#a\n#b", + n: 0, + want: []string{}, + }, + "get 3 lines from 2 line file": { + input: "#a\n#b", + n: 3, + want: []string{"#a", "#b"}, + }, + "get 2 lines from 3 line file": { + input: "#a\n#b\n#c", + n: 2, + want: []string{"#b", "#c"}, + }, + } + + for name, d := range cases { + c := d + t.Run(name, func(t *testing.T) { + p := path.Join(t.TempDir(), base64.URLEncoding.EncodeToString([]byte(t.Name()))) + os.WriteFile(p, []byte(c.input), os.ModePerm) + files, err := NewFiles(p) + if err != nil { + panic(err) + } + if got, err := files.TempGetLastNLines(c.n); err != nil { + t.Fatal(err) + } else if fmt.Sprint(got) != fmt.Sprint(c.want) { + for i := range c.want { + t.Logf("want[%d] = %q", i, c.want[i]) + } + for i := range got { + t.Logf(" got[%d] = %q", i, got[i]) + } + t.Errorf("wanted\n\t%+v, got\n\t%+v", c.want, got) + } + }) + } +} + +func TestFilesTempSetLastNLines(t *testing.T) { + cases := map[string]struct { + given string + input []string + n int + want string + }{ + "empty": {}, + } + + for name, d := range cases { + c := d + t.Run(name, func(t *testing.T) { + p := path.Join(t.TempDir(), base64.URLEncoding.EncodeToString([]byte(t.Name()))) + os.WriteFile(p, []byte(c.given), os.ModePerm) + files := Files([]string{p}) + if err := files.TempSetLastNLines(c.n, c.input); err != nil { + t.Fatal(err) + } + got, _ := os.ReadFile(p) + if string(got) != c.want { + t.Errorf("want\n\t%s, got\n\t%s", c.want, got) + } + }) + } +}