package workspace import ( "fmt" "os" "git.michelsen.id/phill/chron/chron-note/internal/domain" ) func (ws *Workspace) Exists(id domain.ObjectID) (bool, error) { _, err := os.Stat(ws.ObjectPath(id)) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, fmt.Errorf("stat object %s: %w", id.String(), err) } func (ws *Workspace) Read(id domain.ObjectID) ([]byte, error) { b, err := os.ReadFile(ws.ObjectPath(id)) if err != nil { return nil, fmt.Errorf("read object %s: %w", id.String(), err) } return b, nil } func (ws *Workspace) Write(id domain.ObjectID, b []byte) error { if err := os.MkdirAll(ws.Dir, 0o755); err != nil { return fmt.Errorf("mkdir workspace dir: %w", err) } dst := ws.ObjectPath(id) f, err := os.CreateTemp(ws.Dir, id.String()+".*.tmp") if err != nil { return fmt.Errorf("create temp for object %s: %w", id.String(), err) } tmp := f.Name() // Best-effort cleanup on failure. defer func() { _ = os.Remove(tmp) }() if _, err := f.Write(b); err != nil { _ = f.Close() return fmt.Errorf("write temp for object %s: %w", id.String(), err) } if err := f.Close(); err != nil { return fmt.Errorf("close temp for object %s: %w", id.String(), err) } if err := os.Rename(tmp, dst); err != nil { return fmt.Errorf("rename temp for object %s: %w", id.String(), err) } // Rename succeeded; prevent deferred cleanup. _ = os.Remove(tmp) return nil } func (ws *Workspace) Delete(id domain.ObjectID) error { p := ws.ObjectPath(id) err := os.Remove(p) if err == nil || os.IsNotExist(err) { return nil } return fmt.Errorf("delete object %s: %w", id.String(), err) }