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 ` 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)) }