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