package store import ( "database/sql" "errors" "time" ) // Sentinels the HTTP layer maps to error codes. Callers use errors.Is; the // store never constructs api.Error values itself, so that the mapping from // storage failure to wire response lives in exactly one place (internal/adminapi). var ( // ErrNotFound is returned instead of sql.ErrNoRows so callers do not have to // know that the store is backed by database/sql. ErrNotFound = errors.New("store: not found") // ErrExists means a uniqueness constraint rejected the write. ErrExists = errors.New("store: already exists") // ErrConflict means the row was not in the state the operation required. ErrConflict = errors.New("store: conflicting state") ) // mapErr normalises the errors callers are expected to branch on. func mapErr(err error) error { switch { case err == nil: return nil case errors.Is(err, sql.ErrNoRows): return ErrNotFound case IsConstraint(err): return errors.Join(ErrExists, err) default: return err } } // ---------------------------------------------------------- null conversions func nullTime(t *time.Time) any { if t == nil { return nil } return t.Unix() } func timePtr(n sql.NullInt64) *time.Time { if !n.Valid { return nil } t := time.Unix(n.Int64, 0).UTC() return &t } func nullInt(p *int64) any { if p == nil { return nil } return *p } func intPtr(n sql.NullInt64) *int64 { if !n.Valid { return nil } v := n.Int64 return &v } func nullString(s string) any { if s == "" { return nil } return s }