diff --git a/src/Database/IndexedDB/Class.purs b/src/Database/IndexedDB/Class.purs new file mode 100644 index 0000000..4844da4 --- /dev/null +++ b/src/Database/IndexedDB/Class.purs @@ -0,0 +1,13 @@ +-- | Opaque typeclass definitions for easy reuse of functions over +-- | imported foreign types. +module Database.IndexedDB.Class where + + +-- | Cursor objects implement the IDBCursor interface. +-- | There is only ever one IDBCursor instance representing a given cursor. +-- | There is no limit on how many cursors can be used at the same time. +class IDBCursor cursor + +-- | A concrete cursor not only shares IDBCursor properties, but also some +-- | specific attributes (see KeyCursor or CursorWithValue). +class (IDBCursor cursor) <= IDBConcreteCursor cursor diff --git a/src/Database/IndexedDB/Core.js b/src/Database/IndexedDB/Core.js index 828636e..0cf0fa5 100644 --- a/src/Database/IndexedDB/Core.js +++ b/src/Database/IndexedDB/Core.js @@ -3,14 +3,6 @@ const toArray = function toArray(xs) { }; -exports._showCursor = function _showCursor(cursor) { - return '(IDBCursor ' + - '{ direction: ' + cursor.direction + - ', key: ' + cursor.key + - ', primaryKey: ' + cursor.primaryKey + - ' })'; -}; - exports._showDatabase = function _showDatabase(db) { return '(IDBDatabase ' + '{ name: ' + db.name + diff --git a/src/Database/IndexedDB/Core.purs b/src/Database/IndexedDB/Core.purs index 6df4e82..b42008d 100644 --- a/src/Database/IndexedDB/Core.purs +++ b/src/Database/IndexedDB/Core.purs @@ -9,17 +9,14 @@ module Database.IndexedDB.Core ( IDB - , CursorDirection(..) , CursorSource(..) , Database , Index - , KeyCursor , KeyRange , KeyPath , ObjectStore , Transaction , TransactionMode(..) - , ValueCursor , module Database.IndexedDB.IDBKey ) where @@ -35,18 +32,6 @@ import Database.IndexedDB.IDBKey foreign import data IDB :: Effect --- | A cursor has a direction that determines whether it moves in monotonically --- | increasing or decreasing order of the record keys when iterated, and if it --- | skips duplicated values when iterating indexes. --- | The direction of a cursor also determines if the cursor initial position is at --- | the start of its source or at its end. -data CursorDirection - = Next - | NextUnique - | Prev - | PrevUnique - - -- | If the source of a cursor is an object store, the effective object store of -- | the cursor is that object store and the effective key of the cursor is the -- | cursor’s position. If the source of a cursor is an index, the effective object @@ -86,11 +71,6 @@ foreign import data Database :: Type foreign import data Index :: Type --- | A cursor is used to iterate over a range of records in an index or an object store --- | in a specific direction. A KeyCursor doesn't hold any value. -foreign import data KeyCursor :: Type - - -- | A key range is a continuous interval over some data type used for keys. foreign import data KeyRange :: Type @@ -104,20 +84,6 @@ foreign import data ObjectStore :: Type foreign import data Transaction :: Type --- | A cursor is used to iterate over a range of records in an index or an object store --- | in a specific direction. A ValueCursor also holds the value corresponding to matching key. -foreign import data ValueCursor :: Type - - -foreign import _showCursor :: forall cursor. cursor -> String -instance showKeyCursor :: Show KeyCursor where - show = _showCursor - - -instance showValueCursor :: Show ValueCursor where - show = _showCursor - - foreign import _showDatabase :: Database -> String @@ -153,15 +119,6 @@ instance showTransaction :: Show Transaction where show = _showTransaction -instance showCursorDirection :: Show CursorDirection where - show x = - case x of - Next -> "next" - NextUnique -> "nextunique" - Prev -> "prev" - PrevUnique -> "prevunique" - - instance showTransactionMode :: Show TransactionMode where show x = case x of @@ -170,16 +127,6 @@ instance showTransactionMode :: Show TransactionMode where VersionChange -> "versionchange" -instance readCursorDirection :: Read CursorDirection where - read s = - case s of - "next" -> Just Next - "nextunique" -> Just NextUnique - "prev" -> Just Prev - "prevunique" -> Just PrevUnique - _ -> Nothing - - instance readTransactionMode :: Read TransactionMode where read s = case s of diff --git a/src/Database/IndexedDB/IDBCursor.js b/src/Database/IndexedDB/IDBCursor.js index 8845888..1ab42da 100644 --- a/src/Database/IndexedDB/IDBCursor.js +++ b/src/Database/IndexedDB/IDBCursor.js @@ -11,6 +11,14 @@ const successHandler = function successHandler(cb) { }; +exports._showCursor = function _showCursor(cursor) { + return '(IDBCursor ' + + '{ direction: ' + cursor.direction + + ', key: ' + cursor.key + + ', primaryKey: ' + cursor.primaryKey + + ' })'; +}; + exports._advance = function _advance(cursor, count) { return function aff(success, error) { try { diff --git a/src/Database/IndexedDB/IDBCursor.purs b/src/Database/IndexedDB/IDBCursor.purs index 8da88de..4de6455 100644 --- a/src/Database/IndexedDB/IDBCursor.purs +++ b/src/Database/IndexedDB/IDBCursor.purs @@ -1,126 +1,130 @@ -- | A cursor is used to iterate over a range of records in an index or -- | an object store in a specific direction. module Database.IndexedDB.IDBCursor - ( class IDBCursor, advance, continue, continuePrimaryKey, delete, update + -- * Cursor Manipulation + ( KeyCursor + , ValueCursor + , CursorDirection(..) + , advance + , continue + , continuePrimaryKey + , delete + , update + + -- * Concrete Cursor Manipulation , direction , key , primaryKey , source - , direction' - , key' - , primaryKey' - , source' , value ) where -import Prelude (Unit, ($), (>>>), map) +import Prelude (class Show, Unit, ($), (>>>), map) import Control.Monad.Aff (Aff) import Data.Foreign (Foreign, toForeign, unsafeFromForeign) import Data.Function.Uncurried as Fn import Data.Function.Uncurried (Fn2, Fn3) -import Data.Maybe (Maybe) +import Data.Maybe (Maybe(..)) import Data.Nullable (Nullable, toNullable) -import Data.String.Read (read) +import Data.String.Read (class Read, read) import Database.IndexedDB.Core import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(..), toKey, extractForeign) +import Database.IndexedDB.Class (class IDBCursor, class IDBConcreteCursor) + + +-------------------- +-- TYPES +-- + +-- | A cursor is used to iterate over a range of records in an index or an object store +-- | in a specific direction. A KeyCursor doesn't hold any value. +foreign import data KeyCursor :: Type + + +-- | A cursor is used to iterate over a range of records in an index or an object store +-- | in a specific direction. A ValueCursor also holds the value corresponding to matching key. +foreign import data ValueCursor :: Type + + +-- | A cursor has a direction that determines whether it moves in monotonically +-- | increasing or decreasing order of the record keys when iterated, and if it +-- | skips duplicated values when iterating indexes. +-- | The direction of a cursor also determines if the cursor initial position is at +-- | the start of its source or at its end. +data CursorDirection + = Next + | NextUnique + | Prev + | PrevUnique -------------------- -- INTERFACES -- --- | Cursor objects implement the IDBCursor interface. --- | There is only ever one IDBCursor instance representing a given cursor. --- | There is no limit on how many cursors can be used at the same time. -class IDBCursor cursor where - -- | Advances the cursor through the next count records in range. - advance - :: forall e - . cursor - -> Int - -> Aff (idb :: IDB | e) Unit - -- | Advances the cursor to the next record in range matching or after key. - continue - :: forall e k. (IDBKey k) - => cursor - -> Maybe k - -> Aff (idb :: IDB | e) Unit +-- | Advances the cursor through the next count records in range. +advance + :: forall e cursor. (IDBCursor cursor) + => cursor + -> Int + -> Aff (idb :: IDB | e) Unit +advance = + Fn.runFn2 _advance - -- | Advances the cursor to the next record in range matching or after key and primaryKey. Throws an "InvalidAccessError" DOMException if the source is not an index. - continuePrimaryKey - :: forall e k. (IDBKey k) - => cursor - -> k - -> k - -> Aff (idb :: IDB | e) Unit - -- | Delete the record pointed at by the cursor with a new value. - delete - :: forall e - . cursor - -> Aff (idb :: IDB | e) Unit +-- | Advances the cursor to the next record in range matching or after key. +continue + :: forall e k cursor. (IDBKey k) => (IDBCursor cursor) + => cursor + -> Maybe k + -> Aff (idb :: IDB | e) Unit +continue c mk = + Fn.runFn2 _continue c (toNullable $ map (toKey >>> extractForeign) mk) - -- | Update the record pointed at by the cursor with a new value. - -- | - -- | Throws a "DataError" DOMException if the effective object store uses - -- | in-line keys and the key would have changed. - update - :: forall val e - . cursor - -> val - -> Aff (idb :: IDB | e) Key + +-- | Advances the cursor to the next record in range matching or after key and primaryKey. Throws an "InvalidAccessError" DOMException if the source is not an index. +continuePrimaryKey + :: forall e k cursor. (IDBKey k) => (IDBCursor cursor) + => cursor + -> k + -> k + -> Aff (idb :: IDB | e) Unit +continuePrimaryKey c k1 k2 = + Fn.runFn3 _continuePrimaryKey c (extractForeign $ toKey k1) (extractForeign $ toKey k2) + + +-- | Delete the record pointed at by the cursor with a new value. +delete + :: forall e cursor. (IDBCursor cursor) + => cursor + -> Aff (idb :: IDB | e) Unit +delete = + _delete + + +-- | Update the record pointed at by the cursor with a new value. +-- | +-- | Throws a "DataError" DOMException if the effective object store uses +-- | in-line keys and the key would have changed. +update + :: forall val e cursor. (IDBCursor cursor) + => cursor + -> val + -> Aff (idb :: IDB | e) Key +update c = + toForeign >>> Fn.runFn2 _update c >>> map Key -------------------- -- ATTRIBUTES -- --- | Returns the direction (Next|NextUnique|Prev|PrevUnique) of the cursor. -direction' - :: KeyCursor - -> CursorDirection -direction' = - Fn.runFn2 _direction (read >>> toNullable) - - --- | Returns the key of the cursor. Throws a "InvalidStateError" DOMException --- | if the cursor is advancing or is finished. -key' - :: forall e - . KeyCursor - -> Aff (idb :: IDB | e) Key -key' = - _key >>> map Key - - --- | Returns the effective key of the cursor. Throws a "InvalidStateError" DOMException --- | if the cursor is advancing or is finished. -primaryKey' - :: forall e - . KeyCursor - -> Aff (idb :: IDB | e) Key -primaryKey' = - _primaryKey >>> map Key - - --- | Returns the IDBObjectStore or IDBIndex the cursor was opened from. -source' - :: KeyCursor - -> CursorSource -source' = - Fn.runFn3 _source ObjectStore Index - - --- NOTE: Polymorphism not used here cause *we want* those methods to be distincs for --- the concrete types mostly for consistency with the Database.IndexedDB.IDBIndex module --- where the IDBIndex interfaces is shared by Index and ObjectStore, but attributes of --- respective concrete types aren't shared. - -- | Returns the direction (Next|NextUnique|Prev|PrevUnique) of the cursor. direction - :: ValueCursor + :: forall cursor. (IDBConcreteCursor cursor) + => cursor -> CursorDirection direction = Fn.runFn2 _direction (read >>> toNullable) @@ -129,8 +133,8 @@ direction = -- | Returns the key of the cursor. Throws a "InvalidStateError" DOMException -- | if the cursor is advancing or is finished. key - :: forall e - . ValueCursor + :: forall e cursor. (IDBConcreteCursor cursor) + => cursor -> Aff (idb :: IDB | e) Key key = _key >>> map Key @@ -139,8 +143,8 @@ key = -- | Returns the effective key of the cursor. Throws a "InvalidStateError" DOMException -- | if the cursor is advancing or is finished. primaryKey - :: forall e - . ValueCursor + :: forall e cursor. (IDBConcreteCursor cursor) + => cursor -> Aff (idb :: IDB | e) Key primaryKey = _primaryKey >>> map Key @@ -148,7 +152,8 @@ primaryKey = -- | Returns the IDBObjectStore or IDBIndex the cursor was opened from. source - :: ValueCursor + :: forall cursor. (IDBConcreteCursor cursor) + => cursor -> CursorSource source = Fn.runFn3 _source ObjectStore Index @@ -165,25 +170,57 @@ value = -------------------- -- INSTANCES -- -instance keyCursorKeyCursor :: IDBCursor KeyCursor where - advance = Fn.runFn2 _advance - continue c mk = Fn.runFn2 _continue c (toNullable $ map (toKey >>> extractForeign) mk) - continuePrimaryKey c k1 k2 = Fn.runFn3 _continuePrimaryKey c (extractForeign $ toKey k1) (extractForeign $ toKey k2) - delete = _delete - update c = toForeign >>> Fn.runFn2 _update c >>> map Key + +instance idbCursorKeyCursor :: IDBCursor KeyCursor -instance valueCursorKeyCursor :: IDBCursor ValueCursor where - advance = Fn.runFn2 _advance - continue c mk = Fn.runFn2 _continue c (toNullable $ map (toKey >>> extractForeign) mk) - continuePrimaryKey c k1 k2 = Fn.runFn3 _continuePrimaryKey c (extractForeign $ toKey k1) (extractForeign $ toKey k2) - delete = _delete - update c = toForeign >>> Fn.runFn2 _update c >>> map Key +instance idbCursorValueCursor :: IDBCursor ValueCursor + + +instance idbConcreteCursorKeyCursor :: IDBConcreteCursor KeyCursor + + +instance idbConcreteCursorValueCursor :: IDBConcreteCursor ValueCursor + + +instance showKeyCursor :: Show KeyCursor where + show = _showCursor + + +instance showValueCursor :: Show ValueCursor where + show = _showCursor + + +instance showCursorDirection :: Show CursorDirection where + show x = + case x of + Next -> "next" + NextUnique -> "nextunique" + Prev -> "prev" + PrevUnique -> "prevunique" + + + +instance readCursorDirection :: Read CursorDirection where + read s = + case s of + "next" -> Just Next + "nextunique" -> Just NextUnique + "prev" -> Just Prev + "prevunique" -> Just PrevUnique + _ -> Nothing -------------------- -- FFI -- + +foreign import _showCursor + :: forall cursor + . cursor + -> String + + foreign import _advance :: forall cursor e . Fn2 cursor Int (Aff (idb :: IDB | e) Unit) diff --git a/src/Database/IndexedDB/IDBIndex/Internal.purs b/src/Database/IndexedDB/IDBIndex/Internal.purs index 9698a16..864701c 100644 --- a/src/Database/IndexedDB/IDBIndex/Internal.purs +++ b/src/Database/IndexedDB/IDBIndex/Internal.purs @@ -13,7 +13,8 @@ import Data.Function.Uncurried (Fn2, Fn3, Fn4) import Data.Maybe (Maybe) import Data.Nullable (Nullable, toMaybe, toNullable) -import Database.IndexedDB.Core (IDB, CursorDirection, Index, Key, KeyCursor, KeyRange, KeyPath, ObjectStore, ValueCursor) +import Database.IndexedDB.Core (IDB, Index, Key, KeyRange, KeyPath, ObjectStore) +import Database.IndexedDB.IDBCursor (CursorDirection, KeyCursor, ValueCursor) import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(..))