cursor.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. //go:build js && wasm
  2. // +build js,wasm
  3. package idb
  4. import (
  5. "syscall/js"
  6. "github.com/hack-pad/go-indexeddb/idb/internal/jscache"
  7. "github.com/hack-pad/safejs"
  8. )
  9. var (
  10. jsObjectStore safejs.Value
  11. cursorDirectionCache jscache.Strings
  12. )
  13. func init() {
  14. var err error
  15. jsObjectStore, err = safejs.Global().Get("IDBObjectStore")
  16. if err != nil {
  17. panic(err)
  18. }
  19. }
  20. // CursorDirection is the direction of traversal of the cursor
  21. type CursorDirection int
  22. const (
  23. // CursorNext direction causes the cursor to be opened at the start of the source.
  24. CursorNext CursorDirection = iota
  25. // CursorNextUnique direction causes the cursor to be opened at the start of the source. For every key with duplicate values, only the first record is yielded.
  26. CursorNextUnique
  27. // CursorPrevious direction causes the cursor to be opened at the end of the source.
  28. CursorPrevious
  29. // CursorPreviousUnique direction causes the cursor to be opened at the end of the source. For every key with duplicate values, only the first record is yielded.
  30. CursorPreviousUnique
  31. )
  32. func parseCursorDirection(s string) CursorDirection {
  33. switch s {
  34. case "nextunique":
  35. return CursorNextUnique
  36. case "prev":
  37. return CursorPrevious
  38. case "prevunique":
  39. return CursorPreviousUnique
  40. default:
  41. return CursorNext
  42. }
  43. }
  44. func (d CursorDirection) String() string {
  45. switch d {
  46. case CursorNextUnique:
  47. return "nextunique"
  48. case CursorPrevious:
  49. return "prev"
  50. case CursorPreviousUnique:
  51. return "prevunique"
  52. default:
  53. return "next"
  54. }
  55. }
  56. func (d CursorDirection) jsValue() safejs.Value {
  57. return cursorDirectionCache.Value(d.String())
  58. }
  59. // Cursor represents a cursor for traversing or iterating over multiple records in a Database
  60. type Cursor struct {
  61. txn *Transaction
  62. jsCursor safejs.Value
  63. iterated bool // set to true when an iteration method is called, like Continue
  64. }
  65. func wrapCursor(txn *Transaction, jsCursor safejs.Value) *Cursor {
  66. if txn == nil {
  67. txn = (*Transaction)(nil)
  68. }
  69. return &Cursor{
  70. txn: txn,
  71. jsCursor: jsCursor,
  72. }
  73. }
  74. // Source returns the ObjectStore or Index that the cursor is iterating
  75. func (c *Cursor) Source() (objectStore *ObjectStore, index *Index, err error) {
  76. jsSource, err := c.jsCursor.Get("source")
  77. if err != nil {
  78. return
  79. }
  80. if isInstance, _ := jsSource.InstanceOf(jsObjectStore); isInstance {
  81. objectStore = wrapObjectStore(c.txn, jsSource)
  82. } else if isInstance, _ := jsSource.InstanceOf(jsIDBIndex); isInstance {
  83. index = wrapIndex(c.txn, jsSource)
  84. }
  85. return
  86. }
  87. // Direction returns the direction of traversal of the cursor
  88. func (c *Cursor) Direction() (CursorDirection, error) {
  89. direction, err := c.jsCursor.Get("direction")
  90. if err != nil {
  91. return 0, err
  92. }
  93. directionStr, err := direction.String()
  94. return parseCursorDirection(directionStr), err
  95. }
  96. // Key returns the key for the record at the cursor's position. If the cursor is outside its range, this is set to undefined.
  97. func (c *Cursor) Key() (js.Value, error) {
  98. value, err := c.jsCursor.Get("key")
  99. return safejs.Unsafe(value), err
  100. }
  101. // PrimaryKey returns the cursor's current effective primary key. If the cursor is currently being iterated or has iterated outside its range, this is set to undefined.
  102. func (c *Cursor) PrimaryKey() (js.Value, error) {
  103. value, err := c.jsCursor.Get("primaryKey")
  104. return safejs.Unsafe(value), err
  105. }
  106. // Request returns the Request that was used to obtain the cursor.
  107. func (c *Cursor) Request() (*Request, error) {
  108. reqValue, err := c.jsCursor.Get("request")
  109. if err != nil {
  110. return nil, err
  111. }
  112. return wrapRequest(c.txn, reqValue), nil
  113. }
  114. // Advance sets the number of times a cursor should move its position forward.
  115. func (c *Cursor) Advance(count uint) error {
  116. c.iterated = true
  117. _, err := c.jsCursor.Call("advance", count)
  118. return tryAsDOMException(err)
  119. }
  120. // Continue advances the cursor to the next position along its direction.
  121. func (c *Cursor) Continue() error {
  122. c.iterated = true
  123. _, err := c.jsCursor.Call("continue")
  124. return tryAsDOMException(err)
  125. }
  126. // ContinueKey advances the cursor to the next position along its direction.
  127. func (c *Cursor) ContinueKey(key js.Value) error {
  128. c.iterated = true
  129. _, err := c.jsCursor.Call("continue", key)
  130. return tryAsDOMException(err)
  131. }
  132. // ContinuePrimaryKey sets the cursor to the given index key and primary key given as arguments. Returns an error if the source is not an index.
  133. func (c *Cursor) ContinuePrimaryKey(key, primaryKey js.Value) error {
  134. c.iterated = true
  135. _, err := c.jsCursor.Call("continuePrimaryKey", key, primaryKey)
  136. return tryAsDOMException(err)
  137. }
  138. // Delete returns an AckRequest, and, in a separate thread, deletes the record at the cursor's position, without changing the cursor's position. This can be used to delete specific records.
  139. func (c *Cursor) Delete() (*AckRequest, error) {
  140. reqValue, err := c.jsCursor.Call("delete")
  141. if err != nil {
  142. return nil, tryAsDOMException(err)
  143. }
  144. req := wrapRequest(c.txn, reqValue)
  145. return newAckRequest(req), nil
  146. }
  147. // Update returns a Request, and, in a separate thread, updates the value at the current position of the cursor in the object store. This can be used to update specific records.
  148. func (c *Cursor) Update(value js.Value) (*Request, error) {
  149. reqValue, err := c.jsCursor.Call("update", value)
  150. if err != nil {
  151. return nil, tryAsDOMException(err)
  152. }
  153. return wrapRequest(c.txn, reqValue), nil
  154. }
  155. // CursorWithValue represents a cursor for traversing or iterating over multiple records in a database. It is the same as the Cursor, except that it includes the value property.
  156. type CursorWithValue struct {
  157. *Cursor
  158. }
  159. func newCursorWithValue(cursor *Cursor) *CursorWithValue {
  160. return &CursorWithValue{cursor}
  161. }
  162. func wrapCursorWithValue(txn *Transaction, jsCursor safejs.Value) *CursorWithValue {
  163. return newCursorWithValue(wrapCursor(txn, jsCursor))
  164. }
  165. // Value returns the value of the current cursor
  166. func (c *CursorWithValue) Value() (js.Value, error) {
  167. value, err := c.jsCursor.Get("value")
  168. return safejs.Unsafe(value), err
  169. }