package web 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 }