197 lines
5.9 KiB
Go
197 lines
5.9 KiB
Go
//go:build js && wasm
|
|
// +build js,wasm
|
|
|
|
package idb
|
|
|
|
import (
|
|
"syscall/js"
|
|
|
|
"github.com/hack-pad/go-indexeddb/idb/internal/jscache"
|
|
"github.com/hack-pad/safejs"
|
|
)
|
|
|
|
var (
|
|
jsObjectStore safejs.Value
|
|
cursorDirectionCache jscache.Strings
|
|
)
|
|
|
|
func init() {
|
|
var err error
|
|
jsObjectStore, err = safejs.Global().Get("IDBObjectStore")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// CursorDirection is the direction of traversal of the cursor
|
|
type CursorDirection int
|
|
|
|
const (
|
|
// CursorNext direction causes the cursor to be opened at the start of the source.
|
|
CursorNext CursorDirection = iota
|
|
// 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.
|
|
CursorNextUnique
|
|
// CursorPrevious direction causes the cursor to be opened at the end of the source.
|
|
CursorPrevious
|
|
// 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.
|
|
CursorPreviousUnique
|
|
)
|
|
|
|
func parseCursorDirection(s string) CursorDirection {
|
|
switch s {
|
|
case "nextunique":
|
|
return CursorNextUnique
|
|
case "prev":
|
|
return CursorPrevious
|
|
case "prevunique":
|
|
return CursorPreviousUnique
|
|
default:
|
|
return CursorNext
|
|
}
|
|
}
|
|
|
|
func (d CursorDirection) String() string {
|
|
switch d {
|
|
case CursorNextUnique:
|
|
return "nextunique"
|
|
case CursorPrevious:
|
|
return "prev"
|
|
case CursorPreviousUnique:
|
|
return "prevunique"
|
|
default:
|
|
return "next"
|
|
}
|
|
}
|
|
|
|
func (d CursorDirection) jsValue() safejs.Value {
|
|
return cursorDirectionCache.Value(d.String())
|
|
}
|
|
|
|
// Cursor represents a cursor for traversing or iterating over multiple records in a Database
|
|
type Cursor struct {
|
|
txn *Transaction
|
|
jsCursor safejs.Value
|
|
iterated bool // set to true when an iteration method is called, like Continue
|
|
}
|
|
|
|
func wrapCursor(txn *Transaction, jsCursor safejs.Value) *Cursor {
|
|
if txn == nil {
|
|
txn = (*Transaction)(nil)
|
|
}
|
|
return &Cursor{
|
|
txn: txn,
|
|
jsCursor: jsCursor,
|
|
}
|
|
}
|
|
|
|
// Source returns the ObjectStore or Index that the cursor is iterating
|
|
func (c *Cursor) Source() (objectStore *ObjectStore, index *Index, err error) {
|
|
jsSource, err := c.jsCursor.Get("source")
|
|
if err != nil {
|
|
return
|
|
}
|
|
if isInstance, _ := jsSource.InstanceOf(jsObjectStore); isInstance {
|
|
objectStore = wrapObjectStore(c.txn, jsSource)
|
|
} else if isInstance, _ := jsSource.InstanceOf(jsIDBIndex); isInstance {
|
|
index = wrapIndex(c.txn, jsSource)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Direction returns the direction of traversal of the cursor
|
|
func (c *Cursor) Direction() (CursorDirection, error) {
|
|
direction, err := c.jsCursor.Get("direction")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
directionStr, err := direction.String()
|
|
return parseCursorDirection(directionStr), err
|
|
}
|
|
|
|
// Key returns the key for the record at the cursor's position. If the cursor is outside its range, this is set to undefined.
|
|
func (c *Cursor) Key() (js.Value, error) {
|
|
value, err := c.jsCursor.Get("key")
|
|
return safejs.Unsafe(value), err
|
|
}
|
|
|
|
// 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.
|
|
func (c *Cursor) PrimaryKey() (js.Value, error) {
|
|
value, err := c.jsCursor.Get("primaryKey")
|
|
return safejs.Unsafe(value), err
|
|
}
|
|
|
|
// Request returns the Request that was used to obtain the cursor.
|
|
func (c *Cursor) Request() (*Request, error) {
|
|
reqValue, err := c.jsCursor.Get("request")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return wrapRequest(c.txn, reqValue), nil
|
|
}
|
|
|
|
// Advance sets the number of times a cursor should move its position forward.
|
|
func (c *Cursor) Advance(count uint) error {
|
|
c.iterated = true
|
|
_, err := c.jsCursor.Call("advance", count)
|
|
return tryAsDOMException(err)
|
|
}
|
|
|
|
// Continue advances the cursor to the next position along its direction.
|
|
func (c *Cursor) Continue() error {
|
|
c.iterated = true
|
|
_, err := c.jsCursor.Call("continue")
|
|
return tryAsDOMException(err)
|
|
}
|
|
|
|
// ContinueKey advances the cursor to the next position along its direction.
|
|
func (c *Cursor) ContinueKey(key js.Value) error {
|
|
c.iterated = true
|
|
_, err := c.jsCursor.Call("continue", key)
|
|
return tryAsDOMException(err)
|
|
}
|
|
|
|
// 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.
|
|
func (c *Cursor) ContinuePrimaryKey(key, primaryKey js.Value) error {
|
|
c.iterated = true
|
|
_, err := c.jsCursor.Call("continuePrimaryKey", key, primaryKey)
|
|
return tryAsDOMException(err)
|
|
}
|
|
|
|
// 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.
|
|
func (c *Cursor) Delete() (*AckRequest, error) {
|
|
reqValue, err := c.jsCursor.Call("delete")
|
|
if err != nil {
|
|
return nil, tryAsDOMException(err)
|
|
}
|
|
req := wrapRequest(c.txn, reqValue)
|
|
return newAckRequest(req), nil
|
|
}
|
|
|
|
// 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.
|
|
func (c *Cursor) Update(value js.Value) (*Request, error) {
|
|
reqValue, err := c.jsCursor.Call("update", value)
|
|
if err != nil {
|
|
return nil, tryAsDOMException(err)
|
|
}
|
|
return wrapRequest(c.txn, reqValue), nil
|
|
}
|
|
|
|
// 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.
|
|
type CursorWithValue struct {
|
|
*Cursor
|
|
}
|
|
|
|
func newCursorWithValue(cursor *Cursor) *CursorWithValue {
|
|
return &CursorWithValue{cursor}
|
|
}
|
|
|
|
func wrapCursorWithValue(txn *Transaction, jsCursor safejs.Value) *CursorWithValue {
|
|
return newCursorWithValue(wrapCursor(txn, jsCursor))
|
|
}
|
|
|
|
// Value returns the value of the current cursor
|
|
func (c *CursorWithValue) Value() (js.Value, error) {
|
|
value, err := c.jsCursor.Get("value")
|
|
return safejs.Unsafe(value), err
|
|
}
|