module Erebos.Storage.List ( StoredList, emptySList, fromSList, storedFromSList, slistAdd, slistAddS, -- TODO slistInsert, slistInsertS, slistRemove, slistReplace, slistReplaceS, -- TODO mapFromSList, updateOld, -- TODO StoreUpdate(..), -- TODO withStoredListItem, withStoredListItemS, ) where import Data.List import Data.Maybe import qualified Data.Set as S import Erebos.Storage import Erebos.Storage.Internal import Erebos.Storage.Merge data List a = ListNil | ListItem { listPrev :: [StoredList a] , listItem :: Maybe (Stored a) , listRemove :: Maybe (Stored (List a)) } type StoredList a = Stored (List a) instance Storable a => Storable (List a) where store' ListNil = storeZero store' x@ListItem {} = storeRec $ do mapM_ (storeRef "PREV") $ listPrev x mapM_ (storeRef "item") $ listItem x mapM_ (storeRef "remove") $ listRemove x load' = loadCurrentObject >>= \case ZeroObject -> return ListNil _ -> loadRec $ ListItem <$> loadRefs "PREV" <*> loadMbRef "item" <*> loadMbRef "remove" instance Storable a => ZeroStorable (List a) where fromZero _ = ListNil emptySList :: Storable a => Storage -> IO (StoredList a) emptySList st = wrappedStore st ListNil groupsFromSLists :: forall a. Storable a => StoredList a -> [[Stored a]] groupsFromSLists = helperSelect S.empty . (:[]) where helperSelect :: S.Set (StoredList a) -> [StoredList a] -> [[Stored a]] helperSelect rs xxs | x:xs <- sort $ filterRemoved rs xxs = helper rs x xs | otherwise = [] helper :: S.Set (StoredList a) -> StoredList a -> [StoredList a] -> [[Stored a]] helper rs x xs | ListNil <- fromStored x = [] | Just rm <- listRemove (fromStored x) , ans <- ancestors [x] , (other, collision) <- partition (S.null . S.intersection ans . ancestors . (:[])) xs , cont <- helperSelect (rs `S.union` ancestors [rm]) $ concatMap (listPrev . fromStored) (x : collision) ++ other = case catMaybes $ map (listItem . fromStored) (x : collision) of [] -> cont xis -> xis : cont | otherwise = case listItem (fromStored x) of Nothing -> helperSelect rs $ listPrev (fromStored x) ++ xs Just xi -> [xi] : (helperSelect rs $ listPrev (fromStored x) ++ xs) filterRemoved :: S.Set (StoredList a) -> [StoredList a] -> [StoredList a] filterRemoved rs = filter (S.null . S.intersection rs . ancestors . (:[])) fromSList :: Mergeable a => StoredList (Component a) -> [a] fromSList = map merge . groupsFromSLists storedFromSList :: (Mergeable a, Storable a) => StoredList (Component a) -> IO [Stored a] storedFromSList = mapM storeMerge . groupsFromSLists slistAdd :: Storable a => a -> StoredList a -> IO (StoredList a) slistAdd x prev@(Stored (Ref st _) _) = do sx <- wrappedStore st x slistAddS sx prev slistAddS :: Storable a => Stored a -> StoredList a -> IO (StoredList a) slistAddS sx prev@(Stored (Ref st _) _) = wrappedStore st (ListItem [prev] (Just sx) Nothing) {- TODO slistInsert :: Storable a => Stored a -> a -> StoredList a -> IO (StoredList a) slistInsert after x prev@(Stored (Ref st _) _) = do sx <- wrappedStore st x slistInsertS after sx prev slistInsertS :: Storable a => Stored a -> Stored a -> StoredList a -> IO (StoredList a) slistInsertS after sx prev@(Stored (Ref st _) _) = wrappedStore st $ ListItem Nothing (findSListRef after prev) (Just sx) prev -} slistRemove :: Storable a => Stored a -> StoredList a -> IO (StoredList a) slistRemove rm prev@(Stored (Ref st _) _) = wrappedStore st $ ListItem [prev] Nothing (findSListRef rm prev) slistReplace :: Storable a => Stored a -> a -> StoredList a -> IO (StoredList a) slistReplace rm x prev@(Stored (Ref st _) _) = do sx <- wrappedStore st x slistReplaceS rm sx prev slistReplaceS :: Storable a => Stored a -> Stored a -> StoredList a -> IO (StoredList a) slistReplaceS rm sx prev@(Stored (Ref st _) _) = wrappedStore st $ ListItem [prev] (Just sx) (findSListRef rm prev) findSListRef :: Stored a -> StoredList a -> Maybe (StoredList a) findSListRef _ (Stored _ ListNil) = Nothing findSListRef x cur | listItem (fromStored cur) == Just x = Just cur | otherwise = listToMaybe $ catMaybes $ map (findSListRef x) $ listPrev $ fromStored cur {- TODO mapFromSList :: Storable a => StoredList a -> Map RefDigest (Stored a) mapFromSList list = helper list M.empty where helper :: Storable a => StoredList a -> Map RefDigest (Stored a) -> Map RefDigest (Stored a) helper (Stored _ ListNil) cur = cur helper (Stored _ (ListItem (Just rref) _ (Just x) rest)) cur = let rxref = case load rref of ListItem _ _ (Just rx) _ -> sameType rx x $ storedRef rx _ -> error "mapFromSList: malformed list" in helper rest $ case M.lookup (refDigest $ storedRef x) cur of Nothing -> M.insert (refDigest rxref) x cur Just x' -> M.insert (refDigest rxref) x' cur helper (Stored _ (ListItem _ _ _ rest)) cur = helper rest cur sameType :: a -> a -> b -> b sameType _ _ x = x updateOld :: Map RefDigest (Stored a) -> Stored a -> Stored a updateOld m x = fromMaybe x $ M.lookup (refDigest $ storedRef x) m data StoreUpdate a = StoreKeep | StoreReplace a | StoreRemove withStoredListItem :: (Storable a) => (a -> Bool) -> StoredList a -> (a -> IO (StoreUpdate a)) -> IO (StoredList a) withStoredListItem p list f = withStoredListItemS (p . fromStored) list (suMap (wrappedStore $ storedStorage list) <=< f . fromStored) where suMap :: Monad m => (a -> m b) -> StoreUpdate a -> m (StoreUpdate b) suMap _ StoreKeep = return StoreKeep suMap g (StoreReplace x) = return . StoreReplace =<< g x suMap _ StoreRemove = return StoreRemove withStoredListItemS :: (Storable a) => (Stored a -> Bool) -> StoredList a -> (Stored a -> IO (StoreUpdate (Stored a))) -> IO (StoredList a) withStoredListItemS p list f = do case find p $ storedFromSList list of Just sx -> f sx >>= \case StoreKeep -> return list StoreReplace nx -> slistReplaceS sx nx list StoreRemove -> slistRemove sx list Nothing -> return list -}