package sheet
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"intake/internal/normalize"
)
// Store keeps an album's edited sheet between runs.
//
// An edited album used to live only in memory, so quitting the interface, or
// opening a different album and coming back, threw away every correction that
// had been typed. The sheet is already a complete description of what intake is
// about to write, so keeping it on disk is all that is needed to make a review
// resumable, and it is also what makes `intake album <name>` twice in a row do
// the same thing the second time.
//
// The file is named after the staging folder plus a short digest of the whole
// path, so a directory listing stays readable while two albums with the same
// folder name in different places cannot collide.
type Store struct {
Dir string
}
func (s Store) Path(albumDir string) string {
base := normalize.Name(filepath.Base(albumDir))
if base == "" {
base = "album"
}
sum := sha256.Sum256([]byte(filepath.Clean(albumDir)))
return filepath.Join(s.Dir, fmt.Sprintf("%s-%s.txt", base, hex.EncodeToString(sum[:4])))
}
// Save writes the sheet as it stands. It returns the path it wrote, for saying
// where the edits went.
func (s Store) Save(albumDir string, text string) (string, error) {
if s.Dir == "" {
return "", nil
}
if err := os.MkdirAll(s.Dir, 0o755); err != nil {
return "", err
}
path := s.Path(albumDir)
tmp := path + ".tmp"
if err := os.WriteFile(tmp, []byte(text), 0o644); err != nil {
return "", err
}
if err := os.Rename(tmp, path); err != nil {
os.Remove(tmp)
return "", err
}
return path, nil
}
// Load returns the saved sheet text. The second result is false when there is
// none, which is the ordinary case rather than an error: most albums are looked
// at once and filed.
func (s Store) Load(albumDir string) (string, bool, error) {
if s.Dir == "" {
return "", false, nil
}
data, err := os.ReadFile(s.Path(albumDir))
if err != nil {
if os.IsNotExist(err) {
return "", false, nil
}
return "", false, err
}
return string(data), true, nil
}
// Delete drops a saved sheet, once the album it describes has been filed or the
// person looking at it has said to forget it.
func (s Store) Delete(albumDir string) {
if s.Dir == "" {
return
}
os.Remove(s.Path(albumDir))
}