i think files.TempLastNLines are symlink friendly...
This commit is contained in:
@@ -36,26 +36,51 @@ func (files Files) TempGetLastNLines(n int) ([]string, error) {
|
|||||||
|
|
||||||
func (files Files) TempSetLastNLines(n int, lines []string) error {
|
func (files Files) TempSetLastNLines(n int, lines []string) error {
|
||||||
p := files.paths()[0]
|
p := files.paths()[0]
|
||||||
|
|
||||||
|
newFile, err := func() (string, error) {
|
||||||
w, err := ioutil.TempFile(os.TempDir(), path.Base(p))
|
w, err := ioutil.TempFile(os.TempDir(), path.Base(p))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
defer w.Close()
|
defer w.Close()
|
||||||
|
|
||||||
r, err := os.Open(p)
|
r, err := os.Open(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
if _, err := peekLastNLines(w, bufio.NewReader(r), n); err != nil {
|
if _, err := peekLastNLines(w, bufio.NewReader(r), n); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
for i := range lines {
|
for i := range lines {
|
||||||
fmt.Fprintln(w, lines[i])
|
if _, err := fmt.Fprintln(w, lines[i]); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return w.Name(), nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.Rename(w.Name(), p)
|
r, err := os.Open(newFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
w, err := os.Create(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(w, r)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func peekLastNLines(w io.Writer, r *bufio.Reader, n int) ([]string, error) {
|
func peekLastNLines(w io.Writer, r *bufio.Reader, n int) ([]string, error) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ledger
|
package ledger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -401,22 +402,39 @@ func TestFilesTempSetLastNLines(t *testing.T) {
|
|||||||
c := d
|
c := d
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
p := path.Join(t.TempDir(), base64.URLEncoding.EncodeToString([]byte(t.Name())))
|
p := path.Join(t.TempDir(), base64.URLEncoding.EncodeToString([]byte(t.Name())))
|
||||||
os.WriteFile(p, []byte(c.given), os.ModePerm)
|
realp := p + ".real"
|
||||||
files := Files([]string{p})
|
|
||||||
if err := files.TempSetLastNLines(c.n, c.input); err != nil {
|
os.WriteFile(realp, []byte(c.given), os.ModePerm)
|
||||||
s := err.Error()
|
if err := os.Symlink(realp, p); err != nil {
|
||||||
if _, err := os.Stat(s); err == nil {
|
|
||||||
got, _ := os.ReadFile(s)
|
|
||||||
if string(got) != c.want {
|
|
||||||
t.Errorf("want\n%s, got\n%s", c.want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if stat, err := os.Lstat(p); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if stat.Mode().IsRegular() {
|
||||||
|
t.Error("p is already a regular file")
|
||||||
|
}
|
||||||
|
|
||||||
|
files := Files([]string{p})
|
||||||
|
if err := files.TempSetLastNLines(c.n, c.input); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
got, _ := os.ReadFile(p)
|
got, _ := os.ReadFile(p)
|
||||||
if string(got) != c.want {
|
if string(got) != c.want {
|
||||||
t.Errorf("want\n%s, got\n%s", c.want, got)
|
t.Errorf("want\n%s, got\n%s", c.want, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
realb, _ := os.ReadFile(realp)
|
||||||
|
b, _ := os.ReadFile(realp)
|
||||||
|
if !bytes.Equal(b, realb) {
|
||||||
|
t.Errorf("%s no longer links to %s", p, realp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat, err := os.Lstat(p); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if stat.Mode().IsRegular() {
|
||||||
|
t.Error("p is now a regular file")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user