{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Data.GI.Gtk.ModelView.ForestStore (
ForestStore(..),
forestStoreNew,
forestStoreNewDND,
forestStoreDefaultDragSourceIface,
forestStoreDefaultDragDestIface,
forestStoreGetValue,
forestStoreGetTree,
forestStoreGetForest,
forestStoreLookup,
forestStoreSetValue,
forestStoreInsert,
forestStoreInsertTree,
forestStoreInsertForest,
forestStoreRemove,
forestStoreClear,
forestStoreChange,
forestStoreChangeM,
) where
import Prelude ()
import Prelude.Compat
import Data.Bits
import Data.Word (Word32)
import Data.Int (Int32)
import Data.Maybe ( fromMaybe, isJust )
import Data.Tree
import Control.Monad ((>=>), when)
import Control.Monad.IO.Class (MonadIO(..))
import Control.Exception (assert)
import Data.IORef
import Foreign.ForeignPtr (ForeignPtr)
import Data.GI.Base.BasicTypes
(ManagedPtr(..), GObject(..), GObject)
import Data.GI.Base.ManagedPtr (withManagedPtr)
import Data.GI.Base.Overloading (HasParentTypes, ParentTypes)
import Data.GI.Gtk.ModelView.Types
import Data.GI.Gtk.ModelView.CustomStore
(customStoreGetStamp, customStoreGetPrivate,
TreeModelIface(..), customStoreNew, DragDestIface(..),
DragSourceIface(..), CustomStore(..), customStoreInvalidateIters)
import GI.GObject.Objects.Object (Object(..))
import GI.Gtk.Interfaces.TreeModel
(treeModelRowDeleted, treeModelRowInserted,
treeModelRowChanged, toTreeModel, TreeModel(..), IsTreeModel(..),
treeModelRowHasChildToggled)
import GI.Gtk.Functions (treeSetRowDragData, treeGetRowDragData)
import GI.Gtk.Structs.TreePath
(TreePath)
import GI.Gtk.Structs.TreeIter
(getTreeIterUserData3, getTreeIterUserData2, getTreeIterUserData,
getTreeIterStamp, setTreeIterUserData3, setTreeIterUserData2,
setTreeIterUserData, setTreeIterStamp, TreeIter(..))
import Data.GI.Base (get, new)
import Unsafe.Coerce (unsafeCoerce)
data ForestStoreIter = ForestStoreIter Int32 Word32 Word32 Word32
fromForestStoreIter :: MonadIO m => ForestStoreIter -> m TreeIter
fromForestStoreIter (ForestStoreIter s u1 u2 u3) = do
i <- new TreeIter []
setTreeIterStamp i s
setTreeIterUserData i $ unsafeCoerce u1
setTreeIterUserData2 i $ unsafeCoerce u2
setTreeIterUserData3 i $ unsafeCoerce u3
return i
toForestStoreIter :: MonadIO m => TreeIter -> m ForestStoreIter
toForestStoreIter iter = do
stamp <- getTreeIterStamp iter
u1 <- getTreeIterUserData iter
u2 <- getTreeIterUserData2 iter
u3 <- getTreeIterUserData3 iter
return $ ForestStoreIter stamp (unsafeCoerce u1) (unsafeCoerce u2) (unsafeCoerce u3)
forestStoreIterSetStamp :: ForestStoreIter -> Int32 -> ForestStoreIter
forestStoreIterSetStamp (ForestStoreIter _ a b c) s = ForestStoreIter s a b c
newtype ForestStore a = ForestStore (ManagedPtr (CustomStore (IORef (Store a)) a))
mkForestStore :: CustomStore (IORef (Store a)) a -> ForestStore a
mkForestStore (CustomStore ptr) = ForestStore ptr
instance HasParentTypes (ForestStore a)
type instance ParentTypes (ForestStore a) = '[TreeModel]
instance GObject (ForestStore a) where
#if !MIN_VERSION_haskell_gi_base(0,20,1)
gobjectIsInitiallyUnowned _ = False
#endif
gobjectType = gobjectType @TreeModel
instance IsTypedTreeModel ForestStore
type Depth = [Int]
data Store a = Store {
depth :: Depth,
content :: Cache a
}
forestStoreNew :: MonadIO m => Forest a -> m (ForestStore a)
forestStoreNew forest = forestStoreNewDND forest
(Just forestStoreDefaultDragSourceIface)
(Just forestStoreDefaultDragDestIface)
forestStoreNewDND :: MonadIO m => Forest a
-> Maybe (DragSourceIface ForestStore a)
-> Maybe (DragDestIface ForestStore a)
-> m (ForestStore a)
forestStoreNewDND forest mDSource mDDest = liftIO $ do
(storeRef :: IORef (Store a)) <- newIORef Store {
depth = calcForestDepth forest,
content = storeToCache forest
}
let withStore :: (Store a -> IO result) -> IO result
withStore f = readIORef storeRef >>= f
withStoreUpdateCache :: (Store a -> (result, Cache a)) -> IO result
withStoreUpdateCache f = do
store <- readIORef storeRef
let (result, cache') = f store
writeIORef storeRef store { content = cache' }
return result
customStoreNew storeRef mkForestStore TreeModelIface {
treeModelIfaceGetFlags = return [],
treeModelIfaceGetIter = \path -> withStore $
\Store { depth = d } -> fromPath d <$> treePathGetIndices' path >>= mapM fromForestStoreIter,
treeModelIfaceGetPath = toForestStoreIter >=> \iter -> withStore $
\Store { depth = d } -> treePathNewFromIndices' $ toPath d iter,
treeModelIfaceGetRow = toForestStoreIter >=> \iter -> withStoreUpdateCache $
\Store { depth = d, content = cache } ->
case checkSuccess d iter cache of
(True, cache'@((_, (Node { rootLabel = val }:_)):_)) ->
(val, cache')
_ -> error "ForestStore.getRow: iter does not refer to a valid entry",
treeModelIfaceIterNext = toForestStoreIter >=> \iter -> withStoreUpdateCache (
\Store { depth = d, content = cache } -> iterNext d iter cache) >>= mapM fromForestStoreIter,
treeModelIfaceIterChildren = \mIter -> do
iter <- maybe (return invalidIter) toForestStoreIter mIter
withStoreUpdateCache (
\Store { depth = d, content = cache } ->
iterNthChild d 0 iter cache) >>= mapM fromForestStoreIter,
treeModelIfaceIterHasChild = toForestStoreIter >=> \iter -> withStoreUpdateCache $
\Store { depth = d, content = cache } ->
let (mIter, cache') = iterNthChild d 0 iter cache
in (isJust mIter, cache'),
treeModelIfaceIterNChildren = mapM toForestStoreIter >=> \mIter -> withStoreUpdateCache $
\Store { depth = d, content = cache } ->
let iter = fromMaybe invalidIter mIter
in iterNChildren d iter cache,
treeModelIfaceIterNthChild = \mIter idx -> do
iter <- maybe (return invalidIter) toForestStoreIter mIter
withStoreUpdateCache (
\Store { depth = d, content = cache } ->
iterNthChild d idx iter cache) >>= mapM fromForestStoreIter,
treeModelIfaceIterParent = toForestStoreIter >=> \iter -> withStore $
\Store { depth = d } -> mapM fromForestStoreIter (iterParent d iter),
treeModelIfaceRefNode = \_ -> return (),
treeModelIfaceUnrefNode = \_ -> return ()
} mDSource mDDest
forestStoreDefaultDragSourceIface :: DragSourceIface ForestStore row
forestStoreDefaultDragSourceIface = DragSourceIface {
customDragSourceRowDraggable = \_ _-> return True,
customDragSourceDragDataGet = \model path sel -> treeSetRowDragData sel model path,
customDragSourceDragDataDelete = \model path -> treePathGetIndices' path >>= \dest@(_:_) -> do
liftIO $ forestStoreRemove model path
return True
}
forestStoreDefaultDragDestIface :: DragDestIface ForestStore row
forestStoreDefaultDragDestIface = DragDestIface {
customDragDestRowDropPossible = \model path sel -> do
mModelPath <- treeGetRowDragData sel
case mModelPath of
(True, Just model', source) -> do
tm <- toTreeModel model
withManagedPtr tm $ \m ->
withManagedPtr model' $ \m' -> return (m==m')
_ -> return False,
customDragDestDragDataReceived = \model path sel -> do
dest@(_:_) <- treePathGetIndices' path
mModelPath <- treeGetRowDragData sel
case mModelPath of
(True, Just model', Just path) -> do
source@(_:_) <- treePathGetIndices' path
tm <- toTreeModel model
withManagedPtr tm $ \m ->
withManagedPtr model' $ \m' ->
if m/=m' then return False
else do
row <- forestStoreGetTree model =<< treePathNewFromIndices' source
initPath <- treePathNewFromIndices' (init dest)
forestStoreInsertTree model initPath (fromIntegral $ last dest) row
return True
_ -> return False
}
bitsNeeded :: Word32 -> Int
bitsNeeded n = bitsNeeded' 0 n
where bitsNeeded' b 0 = b
bitsNeeded' b n = bitsNeeded' (b+1) (n `shiftR` 1)
getBitSlice :: ForestStoreIter -> Int -> Int -> Word32
getBitSlice (ForestStoreIter _ a b c) off count =
getBitSliceWord a off count
.|. getBitSliceWord b (off-32) count
.|. getBitSliceWord c (off-64) count
where getBitSliceWord :: Word32 -> Int -> Int -> Word32
getBitSliceWord word off count =
word `shift` (-off) .&. (1 `shiftL` count - 1)
setBitSlice :: ForestStoreIter -> Int -> Int -> Word32 -> ForestStoreIter
setBitSlice (ForestStoreIter stamp a b c) off count value =
assert (value < 1 `shiftL` count) $
ForestStoreIter stamp
(setBitSliceWord a off count value)
(setBitSliceWord b (off-32) count value)
(setBitSliceWord c (off-64) count value)
where setBitSliceWord :: Word32 -> Int -> Int -> Word32 -> Word32
setBitSliceWord word off count value =
let mask = (1 `shiftL` count - 1) `shift` off
in (word .&. complement mask) .|. (value `shift` off)
invalidIter :: ForestStoreIter
invalidIter = ForestStoreIter 0 0 0 0
calcForestDepth :: Forest a -> Depth
calcForestDepth f = map bitsNeeded $
takeWhile (/=0) $
foldr calcTreeDepth (repeat 0) f
where
calcTreeDepth Node { subForest = f } (d:ds) =
(d+1): zipWith max ds (foldr calcTreeDepth (repeat 0) f)
toPath :: Depth -> ForestStoreIter -> [Int32]
toPath d iter = gP 0 d
where
gP pos [] = []
gP pos (d:ds) = let idx = getBitSlice iter pos d in
if idx==0 then [] else fromIntegral (idx-1) : gP (pos+d) ds
fromPath :: Depth -> [Int32] -> Maybe ForestStoreIter
fromPath = fP 0 invalidIter
where
fP pos ti _ [] = Just ti
fP pos ti [] _ = Nothing
fP pos ti (d:ds) (p:ps) = let idx = fromIntegral (p+1) in
if idx >= bit d then Nothing else
fP (pos+d) (setBitSlice ti pos d idx) ds ps
type Cache a = [(ForestStoreIter, Forest a)]
storeToCache :: Forest a -> Cache a
storeToCache [] = []
storeToCache forest = [(invalidIter, [Node root forest])]
where
root = error "ForestStore.storeToCache: accessed non-exitent root of tree"
cacheToStore :: Cache a -> Forest a
cacheToStore [] = []
cacheToStore cache = case last cache of (_, [Node _ forest]) -> forest
advanceCache :: Depth -> ForestStoreIter -> Cache a -> Cache a
advanceCache depth goal [] = []
advanceCache depth goal cache@((rootIter,_):_) =
moveToSameLevel 0 depth
where
moveToSameLevel pos [] = cache
moveToSameLevel pos (d:ds) =
let
goalIdx = getBitSlice goal pos d
curIdx = getBitSlice rootIter pos d
isNonZero pos d (ti,_) = getBitSlice ti pos d/=0
in
if goalIdx==curIdx then moveToSameLevel (pos+d) ds else
if goalIdx==0 then dropWhile (isNonZero pos d) cache else
if curIdx==0 then moveToChild pos (d:ds) cache else
if goalIdx<curIdx then
moveToChild pos (d:ds) (dropWhile (isNonZero pos d) cache)
else let
moveWithinLevel pos d ((ti,forest):parents) = let
diff = fromIntegral (goalIdx-curIdx)
(dropped, remain) = splitAt diff forest
advance = length dropped
ti' = setBitSlice ti pos d (curIdx+fromIntegral advance)
in
if advance==diff then moveToChild (pos+d) ds ((ti',remain):parents)
else (ti',remain):parents
in moveWithinLevel pos d $ case ds of
[] -> cache
(d':_) -> dropWhile (isNonZero (pos+d) d') cache
moveToChild :: Int -> Depth -> Cache a -> Cache a
moveToChild pos [] cache = cache
moveToChild pos (d:ds) cache@((ti,forest):parents)
| getBitSlice goal pos d == 0 = cache
| otherwise = case forest of
[] -> cache
Node { subForest = children }:_ ->
let
childIdx :: Int
childIdx = fromIntegral (getBitSlice goal pos d)-1
(dropped, remain) = splitAt childIdx children
advanced = length dropped
ti' = setBitSlice ti pos d (fromIntegral advanced+1)
in if advanced<childIdx then ((ti',remain):cache) else
moveToChild (pos+d) ds ((ti',remain):cache)
checkSuccess :: Depth -> ForestStoreIter -> Cache a -> (Bool, Cache a)
checkSuccess depth iter cache = case advanceCache depth iter cache of
cache'@((cur,sibs):_) -> (cmp cur iter && not (null sibs), cache')
[] -> (False, [])
where
cmp (ForestStoreIter _ a1 b1 c1) (ForestStoreIter _ a2 b2 c2) =
a1==a2 && b1==b2 && c2==c2
getTreeIterLeaf :: Depth -> ForestStoreIter -> (Int, Int, Int)
getTreeIterLeaf ds ti = gTIL 0 0 ds
where
gTIL pos dCur (dNext:ds)
| getBitSlice ti (pos+dCur) dNext==0 = (pos,dCur,dNext)
| otherwise = gTIL (pos+dCur) dNext ds
gTIL pos d [] = (pos, d, 0)
iterNext :: Depth -> ForestStoreIter -> Cache a -> (Maybe ForestStoreIter, Cache a)
iterNext depth iter cache = let
(pos,leaf,_child) = getTreeIterLeaf depth iter
curIdx = getBitSlice iter pos leaf
nextIdx = curIdx+1
nextIter = setBitSlice iter pos leaf nextIdx
in
if nextIdx==bit leaf then (Nothing, cache) else
case checkSuccess depth nextIter cache of
(True, cache) -> (Just nextIter, cache)
(False, cache) -> (Nothing, cache)
iterNthChild :: Depth -> Int -> ForestStoreIter -> Cache a ->
(Maybe ForestStoreIter, Cache a)
iterNthChild depth childIdx_ iter cache = let
(pos,leaf,child) = getTreeIterLeaf depth iter
childIdx = fromIntegral childIdx_+1
nextIter = setBitSlice iter (pos+leaf) child childIdx
in
if childIdx>=bit child then (Nothing, cache) else
case checkSuccess depth nextIter cache of
(True, cache) -> (Just nextIter, cache)
(False, cache) -> (Nothing, cache)
iterNChildren :: Depth -> ForestStoreIter -> Cache a -> (Int, Cache a)
iterNChildren depth iter cache = case checkSuccess depth iter cache of
(True, cache@((_,Node { subForest = forest}:_):_)) -> (length forest, cache)
(_, cache) -> (0, cache)
iterParent :: Depth -> ForestStoreIter -> Maybe ForestStoreIter
iterParent depth iter = let
(pos,leaf,_child) = getTreeIterLeaf depth iter
in if pos==0 then Nothing else
if getBitSlice iter pos leaf==0 then Nothing else
Just (setBitSlice iter pos leaf 0)
forestStoreInsertForest :: MonadIO m
=> ForestStore a
-> TreePath
-> Int
-> Forest a
-> m ()
forestStoreInsertForest (ForestStore model) path pos nodes = liftIO $ do
ipath <- treePathGetIndices' path
customStoreInvalidateIters $ CustomStore model
(idx, toggle) <- atomicModifyIORef (customStoreGetPrivate $ CustomStore model) $
\store@Store { depth = d, content = cache } ->
case insertIntoForest (cacheToStore cache) nodes ipath pos of
Nothing -> error ("forestStoreInsertForest: path does not exist " ++ show ipath)
Just (newForest, idx, toggle) ->
let depth = calcForestDepth newForest
in (Store { depth = depth,
content = storeToCache newForest },
(idx, toggle))
Store { depth = depth } <- readIORef (customStoreGetPrivate $ CustomStore model)
let rpath = reverse ipath
stamp <- customStoreGetStamp $ CustomStore model
sequence_ [ let p' = reverse p
Just iter = fromPath depth p'
in do
p'' <- treePathNewFromIndices' p'
treeModelRowInserted (CustomStore model) p'' =<< fromForestStoreIter (forestStoreIterSetStamp iter stamp)
| (i, node) <- zip [idx..] nodes
, p <- paths (fromIntegral i : rpath) node ]
let Just iter = fromPath depth ipath
when toggle $ treeModelRowHasChildToggled (CustomStore model) path
=<< fromForestStoreIter (forestStoreIterSetStamp iter stamp)
where paths :: [Int32] -> Tree a -> [[Int32]]
paths path Node { subForest = ts } =
path : concat [ paths (n:path) t | (n, t) <- zip [0..] ts ]
forestStoreInsertTree :: MonadIO m
=> ForestStore a
-> TreePath
-> Int
-> Tree a
-> m ()
forestStoreInsertTree store path pos node =
forestStoreInsertForest store path pos [node]
forestStoreInsert :: MonadIO m
=> ForestStore a
-> TreePath
-> Int
-> a
-> m ()
forestStoreInsert store path pos node =
forestStoreInsertForest store path pos [Node node []]
insertIntoForest :: Forest a -> Forest a -> [Int32] -> Int ->
Maybe (Forest a, Int, Bool)
insertIntoForest forest nodes [] pos
| pos<0 = Just (forest++nodes, length forest, null forest)
| otherwise = Just (prev++nodes++next, length prev, null forest)
where (prev, next) = splitAt pos forest
insertIntoForest forest nodes (p:ps) pos = case splitAt (fromIntegral p) forest of
(prev, []) -> Nothing
(prev, Node { rootLabel = val,
subForest = for}:next) ->
case insertIntoForest for nodes ps pos of
Nothing -> Nothing
Just (for, pos, toggle) -> Just (prev++Node { rootLabel = val,
subForest = for }:next,
pos, toggle)
forestStoreRemove :: MonadIO m => ForestStore a -> TreePath -> m Bool
forestStoreRemove model path = treePathGetIndices' path >>= forestStoreRemoveImpl model path
forestStoreRemoveImpl :: MonadIO m => ForestStore a -> TreePath -> [Int32] -> m Bool
forestStoreRemoveImpl (ForestStore model) _ [] = return False
forestStoreRemoveImpl (ForestStore model) path ipath = liftIO $ do
customStoreInvalidateIters (CustomStore model)
(found, toggle) <- atomicModifyIORef (customStoreGetPrivate (CustomStore model)) $
\store@Store { depth = d, content = cache } ->
if null cache then (store, (False, False)) else
case deleteFromForest (cacheToStore cache) ipath of
Nothing -> (store, (False, False))
Just (newForest, toggle) ->
(Store { depth = d,
content = storeToCache newForest }, (True, toggle))
when found $ do
when (toggle && not (null ipath)) $ do
Store { depth = depth } <- readIORef (customStoreGetPrivate (CustomStore model))
let iparent = init ipath
Just iter = fromPath depth iparent
parent <- treePathNewFromIndices' iparent
treeModelRowHasChildToggled (CustomStore model) parent =<< fromForestStoreIter iter
treeModelRowDeleted (CustomStore model) path
return found
forestStoreClear :: MonadIO m => ForestStore a -> m ()
forestStoreClear (ForestStore model) = liftIO $ do
customStoreInvalidateIters (CustomStore model)
Store { content = cache } <- readIORef (customStoreGetPrivate (CustomStore model))
let forest = cacheToStore cache
writeIORef (customStoreGetPrivate (CustomStore model)) Store {
depth = calcForestDepth [],
content = storeToCache []
}
let loop (-1) = return ()
loop n = treePathNewFromIndices' [fromIntegral n] >>= treeModelRowDeleted (CustomStore model) >> loop (n-1)
loop (length forest - 1)
deleteFromForest :: Forest a -> [Int32] -> Maybe (Forest a, Bool)
deleteFromForest forest [] = Just ([], False)
deleteFromForest forest (p:ps) =
case splitAt (fromIntegral p) forest of
(prev, kill@Node { rootLabel = val,
subForest = for}:next) ->
if null ps then Just (prev++next, null prev && null next) else
case deleteFromForest for ps of
Nothing -> Nothing
Just (for,toggle) -> Just (prev++Node {rootLabel = val,
subForest = for }:next, toggle)
(prev, []) -> Nothing
forestStoreSetValue :: MonadIO m => ForestStore a -> TreePath -> a -> m ()
forestStoreSetValue store path value = forestStoreChangeM store path (\_ -> return value)
>> return ()
forestStoreChange :: MonadIO m => ForestStore a -> TreePath -> (a -> a) -> m Bool
forestStoreChange store path func = forestStoreChangeM store path (return . func)
forestStoreChangeM :: MonadIO m => ForestStore a -> TreePath -> (a -> m a) -> m Bool
forestStoreChangeM (ForestStore model) path act = do
ipath <- treePathGetIndices' path
customStoreInvalidateIters (CustomStore model)
store@Store { depth = d, content = cache } <-
liftIO $ readIORef (customStoreGetPrivate (CustomStore model))
(store'@Store { depth = d, content = cache }, found) <- do
mRes <- changeForest (cacheToStore cache) act ipath
return $ case mRes of
Nothing -> (store, False)
Just newForest -> (Store { depth = d,
content = storeToCache newForest }, True)
liftIO $ writeIORef (customStoreGetPrivate (CustomStore model)) store'
let Just iter = fromPath d ipath
stamp <- customStoreGetStamp (CustomStore model)
when found $ treeModelRowChanged (CustomStore model) path =<< fromForestStoreIter (forestStoreIterSetStamp iter stamp)
return found
changeForest :: MonadIO m => Forest a -> (a -> m a) -> [Int32] -> m (Maybe (Forest a))
changeForest forest act [] = return Nothing
changeForest forest act (p:ps) = case splitAt (fromIntegral p) forest of
(prev, []) -> return Nothing
(prev, Node { rootLabel = val,
subForest = for}:next) ->
if null ps then do
val' <- act val
return (Just (prev++Node { rootLabel = val',
subForest = for }:next))
else do
mFor <- changeForest for act ps
case mFor of
Nothing -> return Nothing
Just for -> return $ Just (prev++Node { rootLabel = val,
subForest = for }:next)
forestStoreGetValue :: (Applicative m, MonadIO m) => ForestStore a -> TreePath -> m a
forestStoreGetValue model path = rootLabel <$> forestStoreGetTree model path
forestStoreGetTree :: MonadIO m => ForestStore a -> TreePath -> m (Tree a)
forestStoreGetTree (ForestStore model) path = liftIO $ do
ipath <- treePathGetIndices' path
store@Store { depth = d, content = cache } <-
readIORef (customStoreGetPrivate (CustomStore model))
case fromPath d ipath of
(Just iter) -> do
let (res, cache') = checkSuccess d iter cache
writeIORef (customStoreGetPrivate (CustomStore model)) store { content = cache' }
case cache' of
((_,node:_):_) | res -> return node
_ -> fail ("forestStoreGetTree: path does not exist " ++ show ipath)
_ -> fail ("forestStoreGetTree: path does not exist " ++ show ipath)
forestStoreGetForest :: MonadIO m => ForestStore a -> m (Forest a)
forestStoreGetForest (ForestStore model) = liftIO $ do
store@Store { depth = d, content = cache } <-
readIORef (customStoreGetPrivate (CustomStore model))
return $ cacheToStore cache
forestStoreLookup :: MonadIO m => ForestStore a -> TreePath -> m (Maybe (Tree a))
forestStoreLookup (ForestStore model) path = liftIO $ do
ipath <- treePathGetIndices' path
store@Store { depth = d, content = cache } <-
readIORef (customStoreGetPrivate (CustomStore model))
case fromPath d ipath of
(Just iter) -> do
let (res, cache') = checkSuccess d iter cache
writeIORef (customStoreGetPrivate (CustomStore model)) store { content = cache' }
case cache' of
((_,node:_):_) | res -> return (Just node)
_ -> return Nothing
_ -> return Nothing