bel 2023-10-29 09:05:39 -06:00
parent c75bd74823
commit ce5bf71f6b
2 changed files with 119 additions and 0 deletions

View File

@ -1,6 +1,8 @@
package ledger package ledger
import ( import (
"bufio"
"bytes"
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
@ -20,6 +22,41 @@ func NewFiles(p string, q ...string) (Files, error) {
return f, err 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 { func (files Files) paths() []string {
result := make([]string, 0, len(files)) result := make([]string, 0, len(files))
for i := range files { for i := range files {

View File

@ -1,6 +1,8 @@
package ledger package ledger
import ( import (
"encoding/base64"
"fmt"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -290,3 +292,83 @@ func TestFilesOfDir(t *testing.T) {
t.Error(paths) 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)
}
})
}
}