summaryrefslogtreecommitdiff
path: root/vrsite/log.go
diff options
context:
space:
mode:
Diffstat (limited to 'vrsite/log.go')
-rw-r--r--vrsite/log.go56
1 files changed, 56 insertions, 0 deletions
diff --git a/vrsite/log.go b/vrsite/log.go
new file mode 100644
index 0000000..a0a4f3e
--- /dev/null
+++ b/vrsite/log.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "sync"
+)
+
+// logStore is implemented by the production jj sidecar and by the file-backed
+// adapter used in tests. Append's callback makes session note numbering atomic
+// with the content update.
+type logStore interface {
+ Read() ([]byte, error)
+ Generation() (string, error)
+ Append(func(current []byte) ([]byte, error)) error
+ String() string
+}
+
+type fileLog struct {
+ path string
+ mu sync.Mutex
+}
+
+func newFileLog(path string) *fileLog { return &fileLog{path: path} }
+
+func (l *fileLog) String() string { return l.path }
+
+func (l *fileLog) Read() ([]byte, error) { return os.ReadFile(l.path) }
+
+func (l *fileLog) Generation() (string, error) {
+ info, err := os.Stat(l.path)
+ if err != nil {
+ return "", err
+ }
+ return fmt.Sprintf("%d:%d", info.Size(), info.ModTime().UnixNano()), nil
+}
+
+func (l *fileLog) Append(makePayload func(current []byte) ([]byte, error)) error {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ current, err := os.ReadFile(l.path)
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ payload, err := makePayload(current)
+ if err != nil {
+ return err
+ }
+ f, err := os.OpenFile(l.path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = f.Write(payload)
+ return err
+}