{-# LANGUAGE NoImplicitPrelude #-}

module Tendermint.SDK.BaseApp.Store.Array
  ( Array
  , makeArray
  , makeFullStoreKey
  , append
  , length
  , foldl
  , modifyAtIndex
  , deleteWhen
  , (!!)
  , elemIndex
  , toList
  ) where

import           Control.Lens                          (iso, (^.))
import qualified Data.ByteArray.HexString              as Hex
import qualified Data.ByteString                       as BS
import           Data.Kind                             (Type)
import           Data.Maybe                            (fromMaybe)
import           Data.String.Conversions               (cs)
import           Data.Word                             (Word64)
import           Polysemy
import           Polysemy.Error                        (Error)
import           Prelude                               hiding (foldl, length,
                                                        (!!))
import qualified Prelude                               as P (length)
import           Tendermint.SDK.BaseApp.Errors         (AppError,
                                                        SDKError (InternalError),
                                                        throwSDKError)
import           Tendermint.SDK.BaseApp.Store.RawStore as S
import           Tendermint.SDK.Codec                  (HasCodec (..))



-- | A 'Array a' is an appendable list whose elements can be accessed
-- | by their index. You can also delete from the list, in which case accessing
-- | that index will result in a `Nothing`.
data Array (a :: Type) = Array
  { Array a -> Store (Array a)
arrayStore :: S.Store (Array a) }

-- | Represents an index into a list
newtype Idx = Idx {Idx -> Word64
unIdx :: Word64} deriving (Idx -> Idx -> Bool
(Idx -> Idx -> Bool) -> (Idx -> Idx -> Bool) -> Eq Idx
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Idx -> Idx -> Bool
$c/= :: Idx -> Idx -> Bool
== :: Idx -> Idx -> Bool
$c== :: Idx -> Idx -> Bool
Eq, Int -> Idx -> ShowS
[Idx] -> ShowS
Idx -> String
(Int -> Idx -> ShowS)
-> (Idx -> String) -> ([Idx] -> ShowS) -> Show Idx
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Idx] -> ShowS
$cshowList :: [Idx] -> ShowS
show :: Idx -> String
$cshow :: Idx -> String
showsPrec :: Int -> Idx -> ShowS
$cshowsPrec :: Int -> Idx -> ShowS
Show, Eq Idx
Eq Idx =>
(Idx -> Idx -> Ordering)
-> (Idx -> Idx -> Bool)
-> (Idx -> Idx -> Bool)
-> (Idx -> Idx -> Bool)
-> (Idx -> Idx -> Bool)
-> (Idx -> Idx -> Idx)
-> (Idx -> Idx -> Idx)
-> Ord Idx
Idx -> Idx -> Bool
Idx -> Idx -> Ordering
Idx -> Idx -> Idx
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Idx -> Idx -> Idx
$cmin :: Idx -> Idx -> Idx
max :: Idx -> Idx -> Idx
$cmax :: Idx -> Idx -> Idx
>= :: Idx -> Idx -> Bool
$c>= :: Idx -> Idx -> Bool
> :: Idx -> Idx -> Bool
$c> :: Idx -> Idx -> Bool
<= :: Idx -> Idx -> Bool
$c<= :: Idx -> Idx -> Bool
< :: Idx -> Idx -> Bool
$c< :: Idx -> Idx -> Bool
compare :: Idx -> Idx -> Ordering
$ccompare :: Idx -> Idx -> Ordering
$cp1Ord :: Eq Idx
Ord, Integer -> Idx
Idx -> Idx
Idx -> Idx -> Idx
(Idx -> Idx -> Idx)
-> (Idx -> Idx -> Idx)
-> (Idx -> Idx -> Idx)
-> (Idx -> Idx)
-> (Idx -> Idx)
-> (Idx -> Idx)
-> (Integer -> Idx)
-> Num Idx
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
fromInteger :: Integer -> Idx
$cfromInteger :: Integer -> Idx
signum :: Idx -> Idx
$csignum :: Idx -> Idx
abs :: Idx -> Idx
$cabs :: Idx -> Idx
negate :: Idx -> Idx
$cnegate :: Idx -> Idx
* :: Idx -> Idx -> Idx
$c* :: Idx -> Idx -> Idx
- :: Idx -> Idx -> Idx
$c- :: Idx -> Idx -> Idx
+ :: Idx -> Idx -> Idx
$c+ :: Idx -> Idx -> Idx
Num)

instance S.RawKey Idx where
  rawKey :: p ByteString (f ByteString) -> p Idx (f Idx)
rawKey = (Idx -> ByteString) -> (ByteString -> Idx) -> Iso' Idx ByteString
forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso Idx -> ByteString
elementKey ByteString -> Idx
unElementKey

instance S.IsKey Idx (Array a) where
  type Value Idx (Array a) = a

-- Internal, used for accessing list length.
data LengthKey = LengthKey

instance S.RawKey LengthKey where
  rawKey :: p ByteString (f ByteString) -> p LengthKey (f LengthKey)
rawKey = (LengthKey -> ByteString)
-> (ByteString -> LengthKey) -> Iso' LengthKey ByteString
forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (ByteString -> LengthKey -> ByteString
forall a b. a -> b -> a
const ByteString
lengthKey) ByteString -> LengthKey
unLengthKey

instance S.IsKey LengthKey (Array a) where
  type Value LengthKey (Array a) = Word64

-- | Smart constuctor to make sure we're making a 'Array' from
-- | the appropriate key type.
makeArray
  :: S.IsKey k ns
  => S.Value k ns ~ Array a
  => k
  -> S.Store ns
  -> S.Value k ns
makeArray :: k -> Store ns -> Value k ns
makeArray k :: k
k store :: Store ns
store =
  let skr :: S.KeyRoot (Array a)
      skr :: KeyRoot (Array a)
skr = ByteString -> KeyRoot (Array a)
forall k (ns :: k). ByteString -> KeyRoot ns
S.KeyRoot (ByteString -> KeyRoot (Array a))
-> ByteString -> KeyRoot (Array a)
forall a b. (a -> b) -> a -> b
$ k
k k -> Getting ByteString k ByteString -> ByteString
forall s a. s -> Getting a s a -> a
^. Getting ByteString k ByteString
forall k. RawKey k => Iso' k ByteString
S.rawKey
  in Store (Array a) -> Value k ns
forall a. Store (Array a) -> Array a
Array (Store (Array a) -> Value k ns) -> Store (Array a) -> Value k ns
forall a b. (a -> b) -> a -> b
$ Store ns -> Store (Array a) -> Store (Array a)
forall k1 k2 (parentns :: k1) (childns :: k2).
Store parentns -> Store childns -> Store childns
S.nestStore Store ns
store (KeyRoot (Array a) -> Store (Array a)
forall k (ns :: k). KeyRoot ns -> Store ns
S.makeStore KeyRoot (Array a)
forall a. KeyRoot (Array a)
skr)

makeFullStoreKey
  :: Array a
  -> Word64
  -> S.StoreKey
makeFullStoreKey :: Array a -> Word64 -> StoreKey
makeFullStoreKey Array{..} i :: Word64
i =
  Store (Array a) -> Idx -> StoreKey
forall k1 k2 (ns :: k1). IsKey k2 ns => Store ns -> k2 -> StoreKey
S.makeStoreKey Store (Array a)
arrayStore (Word64 -> Idx
Idx Word64
i)

-- | Add an item to the end of the list.
append
  :: Members [Error AppError, S.ReadStore, S.WriteStore] r
  => HasCodec a
  => a
  -> Array a
  -> Sem r ()
append :: a -> Array a -> Sem r ()
append a :: a
a as :: Array a
as@Array{..} = do
  Word64
n <- Array a -> Sem r Word64
forall (r :: [(* -> *) -> * -> *]) a.
Members '[Error AppError, ReadStore] r =>
Array a -> Sem r Word64
length Array a
as
  Idx -> a -> Array a -> Sem r ()
forall (r :: [(* -> *) -> * -> *]) a.
(Members '[Error AppError, ReadStore, WriteStore] r, HasCodec a) =>
Idx -> a -> Array a -> Sem r ()
writeAt (Word64 -> Idx
Idx Word64
n) a
a Array a
as
  Store (Array a)
-> LengthKey -> Value LengthKey (Array a) -> Sem r ()
forall k1 k2 (r :: [(* -> *) -> * -> *]) (ns :: k1).
(IsKey k2 ns, HasCodec (Value k2 ns), Member WriteStore r) =>
Store ns -> k2 -> Value k2 ns -> Sem r ()
S.put Store (Array a)
arrayStore LengthKey
LengthKey (Word64
n Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1)

-- | Access an item directly by its index.
(!!)
  :: Members [Error AppError, S.ReadStore] r
  => HasCodec a
  => Array a
  -> Word64
  -> Sem r (Maybe a)
as :: Array a
as@Array{..} !! :: Array a -> Word64 -> Sem r (Maybe a)
!! i :: Word64
i = do
  let n :: Idx
n = Word64 -> Idx
Idx Word64
i
  Word64
len <- Array a -> Sem r Word64
forall (r :: [(* -> *) -> * -> *]) a.
Members '[Error AppError, ReadStore] r =>
Array a -> Sem r Word64
length Array a
as
  if Word64 -> Idx
Idx (Word64
len Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- 1) Idx -> Idx -> Bool
forall a. Ord a => a -> a -> Bool
< Idx
n
    then Maybe a -> Sem r (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
    else Store (Array a) -> Idx -> Sem r (Maybe (Value Idx (Array a)))
forall k1 k2 (r :: [(* -> *) -> * -> *]) (ns :: k1).
(IsKey k2 ns, HasCodec (Value k2 ns),
 Members '[ReadStore, Error AppError] r) =>
Store ns -> k2 -> Sem r (Maybe (Value k2 ns))
S.get Store (Array a)
arrayStore Idx
n

infixl 9  !!

-- | Modify a list at a particular index, return the
-- | updated value if the element was found.
modifyAtIndex
  :: Members [Error AppError, S.ReadStore, S.WriteStore] r
  => HasCodec a
  => Word64
  -> (a -> a)
  -> Array a
  -> Sem r (Maybe a)
modifyAtIndex :: Word64 -> (a -> a) -> Array a -> Sem r (Maybe a)
modifyAtIndex i :: Word64
i f :: a -> a
f as :: Array a
as = do
  Maybe a
mRes <- Array a
as Array a -> Word64 -> Sem r (Maybe a)
forall (r :: [(* -> *) -> * -> *]) a.
(Members '[Error AppError, ReadStore] r, HasCodec a) =>
Array a -> Word64 -> Sem r (Maybe a)
!! Word64
i
  case Maybe a
mRes of
    Nothing -> Maybe a -> Sem r (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
    Just res :: a
res -> do
      let a' :: a
a' = a -> a
f a
res
      Idx -> a -> Array a -> Sem r ()
forall (r :: [(* -> *) -> * -> *]) a.
(Members '[Error AppError, ReadStore, WriteStore] r, HasCodec a) =>
Idx -> a -> Array a -> Sem r ()
writeAt (Word64 -> Idx
Idx Word64
i) a
a' Array a
as
      Maybe a -> Sem r (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Maybe a
forall a. a -> Maybe a
Just a
a')

-- | Delete when the predicate evaluates to true.
deleteWhen
  :: Members [Error AppError, S.ReadStore, S.WriteStore] r
  => HasCodec a
  => (a -> Bool)
  -> Array a
  -> Sem r ()
deleteWhen :: (a -> Bool) -> Array a -> Sem r ()
deleteWhen p :: a -> Bool
p as :: Array a
as@Array{..} = do
    Word64
len <- Array a -> Sem r Word64
forall (r :: [(* -> *) -> * -> *]) a.
Members '[Error AppError, ReadStore] r =>
Array a -> Sem r Word64
length Array a
as
    Word64 -> Word64 -> Sem r ()
delete' 0 (Word64
len Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- 1)
  where
    delete' :: Word64 -> Word64 -> Sem r ()
delete' n :: Word64
n end :: Word64
end  =
      if Word64
n Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
> Word64
end
        then () -> Sem r ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
        else do
          Maybe a
mRes <- Array a
as Array a -> Word64 -> Sem r (Maybe a)
forall (r :: [(* -> *) -> * -> *]) a.
(Members '[Error AppError, ReadStore] r, HasCodec a) =>
Array a -> Word64 -> Sem r (Maybe a)
!! Word64
n
          case Maybe a
mRes of
            Nothing -> Word64 -> Word64 -> Sem r ()
delete' (Word64
n Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1) Word64
end
            Just res :: a
res ->
              if a -> Bool
p a
res
                then do
                  Store (Array a) -> Idx -> Sem r ()
forall k1 k2 (ns :: k1) (r :: [(* -> *) -> * -> *]).
(IsKey k2 ns, Member WriteStore r) =>
Store ns -> k2 -> Sem r ()
S.delete Store (Array a)
arrayStore (Word64 -> Idx
Idx Word64
n)
                  Word64 -> Word64 -> Sem r ()
delete' (Word64
n Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1) Word64
end
                else Word64 -> Word64 -> Sem r ()
delete' (Word64
n Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1) Word64
end

-- | Get the first index where an element appears in the list.
elemIndex
  :: Members [Error AppError, S.ReadStore] r
  => HasCodec a
  => Eq a
  => a
  -> Array a
  -> Sem r (Maybe Word64)
elemIndex :: a -> Array a -> Sem r (Maybe Word64)
elemIndex a :: a
a as :: Array a
as = do
    Word64
len <- Array a -> Sem r Word64
forall (r :: [(* -> *) -> * -> *]) a.
Members '[Error AppError, ReadStore] r =>
Array a -> Sem r Word64
length Array a
as
    Word64 -> Word64 -> Sem r (Maybe Word64)
elemIndex' 0 Word64
len
  where
    elemIndex' :: Word64 -> Word64 -> Sem r (Maybe Word64)
elemIndex' n :: Word64
n len :: Word64
len
      | Word64
n Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
== Word64
len = Maybe Word64 -> Sem r (Maybe Word64)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe Word64
forall a. Maybe a
Nothing
      | Bool
otherwise = do
          Maybe a
mRes <- Array a
as Array a -> Word64 -> Sem r (Maybe a)
forall (r :: [(* -> *) -> * -> *]) a.
(Members '[Error AppError, ReadStore] r, HasCodec a) =>
Array a -> Word64 -> Sem r (Maybe a)
!! Word64
n
          let keepLooking :: Sem r (Maybe Word64)
keepLooking = Word64 -> Word64 -> Sem r (Maybe Word64)
elemIndex' (Word64
n Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1) Word64
len
          case Maybe a
mRes of
            Nothing -> Sem r (Maybe Word64)
keepLooking
            Just a' :: a
a' -> if a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
a' then Maybe Word64 -> Sem r (Maybe Word64)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Word64 -> Sem r (Maybe Word64))
-> Maybe Word64 -> Sem r (Maybe Word64)
forall a b. (a -> b) -> a -> b
$ Word64 -> Maybe Word64
forall a. a -> Maybe a
Just Word64
n else Sem r (Maybe Word64)
keepLooking

foldl
  :: Members [Error AppError, S.ReadStore] r
  => HasCodec a
  => (b -> a -> b)
  -> b
  -> Array a
  -> Sem r b
foldl :: (b -> a -> b) -> b -> Array a -> Sem r b
foldl f :: b -> a -> b
f b :: b
b as :: Array a
as = do
  Word64
len <- Array a -> Sem r Word64
forall (r :: [(* -> *) -> * -> *]) a.
Members '[Error AppError, ReadStore] r =>
Array a -> Sem r Word64
length Array a
as
  Word64 -> Word64 -> b -> Sem r b
foldl' 0 Word64
len b
b
  where
    foldl' :: Word64 -> Word64 -> b -> Sem r b
foldl' currentIndex :: Word64
currentIndex end :: Word64
end accum :: b
accum
      | Word64
currentIndex Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
== Word64
end = b -> Sem r b
forall (f :: * -> *) a. Applicative f => a -> f a
pure b
accum
      | Word64
currentIndex Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
< Word64
end = do
          Maybe a
ma <- Array a
as Array a -> Word64 -> Sem r (Maybe a)
forall (r :: [(* -> *) -> * -> *]) a.
(Members '[Error AppError, ReadStore] r, HasCodec a) =>
Array a -> Word64 -> Sem r (Maybe a)
!! Word64
currentIndex
          case Maybe a
ma of
            Nothing -> Word64 -> Word64 -> b -> Sem r b
foldl' (Word64
currentIndex Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1) Word64
end b
accum
            Just a :: a
a  -> Word64 -> Word64 -> b -> Sem r b
foldl' (Word64
currentIndex Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ 1) Word64
end (b -> Sem r b) -> b -> Sem r b
forall a b. (a -> b) -> a -> b
$! b -> a -> b
f b
accum a
a
      | Bool
otherwise = String -> Sem r b
forall a. HasCallStack => String -> a
error "Impossible case in Array foldl!"

-- | View the 'Array' as a 'Array'.
toList
  :: Members [Error AppError, S.ReadStore] r
  => HasCodec a
  => Array a
  -> Sem r [a]
toList :: Array a -> Sem r [a]
toList = ([a] -> a -> [a]) -> [a] -> Array a -> Sem r [a]
forall (r :: [(* -> *) -> * -> *]) a b.
(Members '[Error AppError, ReadStore] r, HasCodec a) =>
(b -> a -> b) -> b -> Array a -> Sem r b
foldl ((a -> [a] -> [a]) -> [a] -> a -> [a]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (:)) []

--------------------------------------------------------------------------------
-- Internal functions
--------------------------------------------------------------------------------

-- NOTE: Many things in this module are completely ad hoc, but tries to follow
-- the patterns set in https://github.com/cosmos/cosmos-sdk/blob/master/store/list/list.go
-- for future compatability, and because this ad-hoc-ness doesn't leak out.

lengthKey :: BS.ByteString
lengthKey :: ByteString
lengthKey = HexString -> ByteString
forall ba. ByteArray ba => HexString -> ba
Hex.toBytes "0x00"

unLengthKey :: BS.ByteString -> LengthKey
unLengthKey :: ByteString -> LengthKey
unLengthKey bs :: ByteString
bs
  | ByteString
bs ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== HexString -> ByteString
forall ba. ByteArray ba => HexString -> ba
Hex.toBytes "0x00" = LengthKey
LengthKey
  | Bool
otherwise = String -> LengthKey
forall a. HasCallStack => String -> a
error (String -> LengthKey) -> String -> LengthKey
forall a b. (a -> b) -> a -> b
$ "Couldn't parse LengthKey bytes: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> ByteString -> String
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
bs

elementKey
  :: Idx
  -> BS.ByteString
elementKey :: Idx -> ByteString
elementKey (Idx k :: Word64
k) =
    let padToNChars :: Int -> ShowS
padToNChars n :: Int
n a :: String
a =
          let nZeros :: Int
nZeros = Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
P.length String
a
          in Int -> Char -> String
forall a. Int -> a -> [a]
replicate Int
nZeros '0' String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
a
    in HexString -> ByteString
forall ba. ByteArray ba => HexString -> ba
Hex.toBytes "0x01" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> String -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs (Int -> ShowS
padToNChars 20 ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$ Word64 -> String
forall a. Show a => a -> String
show Word64
k)

unElementKey
  :: BS.ByteString
  -> Idx
unElementKey :: ByteString -> Idx
unElementKey bs :: ByteString
bs =
    let str :: String
str = ByteString -> String
forall a b. ConvertibleStrings a b => a -> b
cs (ByteString -> String) -> ByteString -> String
forall a b. (a -> b) -> a -> b
$ ByteString -> Maybe ByteString -> ByteString
forall a. a -> Maybe a -> a
fromMaybe (String -> ByteString
forall a. HasCallStack => String -> a
error "Idx missing 0x01 prefix") (Maybe ByteString -> ByteString) -> Maybe ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$
                ByteString -> ByteString -> Maybe ByteString
BS.stripPrefix (HexString -> ByteString
forall ba. ByteArray ba => HexString -> ba
Hex.toBytes "0x01") ByteString
bs
    in Word64 -> Idx
Idx (Word64 -> Idx) -> (String -> Word64) -> String -> Idx
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Word64
forall a. Read a => String -> a
read (String -> Word64) -> ShowS -> String -> Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '0') (String -> Idx) -> String -> Idx
forall a b. (a -> b) -> a -> b
$ String
str

length
  :: Members [Error AppError, S.ReadStore] r
  => Array a
  -> Sem r Word64
length :: Array a -> Sem r Word64
length Array{..} = do
  Maybe Word64
mLen <- Store (Array a)
-> LengthKey -> Sem r (Maybe (Value LengthKey (Array a)))
forall k1 k2 (r :: [(* -> *) -> * -> *]) (ns :: k1).
(IsKey k2 ns, HasCodec (Value k2 ns),
 Members '[ReadStore, Error AppError] r) =>
Store ns -> k2 -> Sem r (Maybe (Value k2 ns))
S.get Store (Array a)
arrayStore LengthKey
LengthKey
  Word64 -> Sem r Word64
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word64 -> Sem r Word64) -> Word64 -> Sem r Word64
forall a b. (a -> b) -> a -> b
$ Word64 -> Maybe Word64 -> Word64
forall a. a -> Maybe a -> a
fromMaybe 0 Maybe Word64
mLen

writeAt
  :: Members [Error AppError, S.ReadStore, S.WriteStore] r
  => HasCodec a
  => Idx
  -> a
  -> Array a
  -> Sem r ()
writeAt :: Idx -> a -> Array a -> Sem r ()
writeAt idx :: Idx
idx@(Idx i :: Word64
i) a :: a
a as :: Array a
as@Array{..} = do
  Word64
len <- Array a -> Sem r Word64
forall (r :: [(* -> *) -> * -> *]) a.
Members '[Error AppError, ReadStore] r =>
Array a -> Sem r Word64
length Array a
as
  Word64 -> Sem r ()
writeAt' Word64
len
  where
    writeAt' :: Word64 -> Sem r ()
writeAt' len :: Word64
len
      | Word64
i Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
== Word64
len = do
        Store (Array a) -> Idx -> Value Idx (Array a) -> Sem r ()
forall k1 k2 (r :: [(* -> *) -> * -> *]) (ns :: k1).
(IsKey k2 ns, HasCodec (Value k2 ns), Member WriteStore r) =>
Store ns -> k2 -> Value k2 ns -> Sem r ()
S.put Store (Array a)
arrayStore Idx
idx a
Value Idx (Array a)
a
        Store (Array a)
-> LengthKey -> Value LengthKey (Array a) -> Sem r ()
forall k1 k2 (r :: [(* -> *) -> * -> *]) (ns :: k1).
(IsKey k2 ns, HasCodec (Value k2 ns), Member WriteStore r) =>
Store ns -> k2 -> Value k2 ns -> Sem r ()
S.put Store (Array a)
arrayStore LengthKey
LengthKey Word64
Value LengthKey (Array a)
i
      | Word64
i Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
< Word64
len =
        Store (Array a) -> Idx -> Value Idx (Array a) -> Sem r ()
forall k1 k2 (r :: [(* -> *) -> * -> *]) (ns :: k1).
(IsKey k2 ns, HasCodec (Value k2 ns), Member WriteStore r) =>
Store ns -> k2 -> Value k2 ns -> Sem r ()
S.put Store (Array a)
arrayStore Idx
idx a
Value Idx (Array a)
a
      | Bool
otherwise = SDKError -> Sem r ()
forall (r :: [(* -> *) -> * -> *]) a.
Member (Error AppError) r =>
SDKError -> Sem r a
throwSDKError (SDKError -> Sem r ()) -> SDKError -> Sem r ()
forall a b. (a -> b) -> a -> b
$ Text -> SDKError
InternalError "Cannot write past list length index."