packages feed

mergeless 0.2.0.2 → 0.3.0.0

raw patch · 5 files changed

+271/−250 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Mergeless.Collection: [serverSyncProcessorDeleteMany] :: ServerSyncProcessor i a m -> Set i -> m (Set i)
- Data.Mergeless.Collection: [serverSyncProcessorInsertMany] :: ServerSyncProcessor i a m -> Map ClientId a -> m (Map ClientId i)
- Data.Mergeless.Collection: [serverSyncProcessorQueryNewRemote] :: ServerSyncProcessor i a m -> Set i -> m (Map i a)
- Data.Mergeless.Collection: [serverSyncProcessorQueryNoLongerSynced] :: ServerSyncProcessor i a m -> Set i -> m (Set i)
- Data.Mergeless.Collection: addAddedItems :: forall i a. (Ord i, Ord a) => Map ClientId i -> ClientStore i a -> ClientStore i a
- Data.Mergeless.Collection: addRemotelyAddedItems :: (Ord i, Ord a) => Map i a -> ClientStore i a -> ClientStore i a
- Data.Mergeless.Collection: deleteItemsToBeDeletedLocally :: (Ord i, Ord a) => Set i -> ClientStore i a -> ClientStore i a
- Data.Mergeless.Collection: deleteLocalUndeletedItems :: (Ord i, Ord a) => Set i -> ClientStore i a -> ClientStore i a
- Data.Mergeless.Collection: instance (Control.DeepSeq.NFData i, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.Mergeless.Collection.ClientStore i a)
- Data.Mergeless.Collection: instance (Control.DeepSeq.NFData i, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.Mergeless.Collection.ServerStore i a)
- Data.Mergeless.Collection: instance (Control.DeepSeq.NFData i, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.Mergeless.Collection.SyncRequest i a)
- Data.Mergeless.Collection: instance (Control.DeepSeq.NFData i, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.Mergeless.Collection.SyncResponse i a)
- Data.Mergeless.Collection: instance (Data.Aeson.Types.FromJSON.FromJSON i, Data.Aeson.Types.FromJSON.FromJSON a, GHC.Classes.Ord i, GHC.Classes.Ord a) => Data.Aeson.Types.FromJSON.FromJSON (Data.Mergeless.Collection.SyncRequest i a)
- Data.Mergeless.Collection: instance (Data.Aeson.Types.FromJSON.FromJSONKey i, GHC.Classes.Ord i, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Data.Mergeless.Collection.ServerStore i a)
- Data.Mergeless.Collection: instance (Data.Aeson.Types.ToJSON.ToJSON a, Data.Aeson.Types.ToJSON.ToJSONKey i) => Data.Aeson.Types.ToJSON.ToJSON (Data.Mergeless.Collection.ServerStore i a)
- Data.Mergeless.Collection: instance (Data.Aeson.Types.ToJSON.ToJSON i, Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (Data.Mergeless.Collection.SyncRequest i a)
- Data.Mergeless.Collection: instance (Data.Aeson.Types.ToJSON.ToJSON i, Data.Aeson.Types.ToJSON.ToJSONKey i, Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (Data.Mergeless.Collection.SyncResponse i a)
- Data.Mergeless.Collection: instance (Data.Validity.Validity i, Data.Validity.Validity a, GHC.Classes.Ord i, GHC.Classes.Ord a) => Data.Validity.Validity (Data.Mergeless.Collection.SyncRequest i a)
- Data.Mergeless.Collection: instance (Data.Validity.Validity i, Data.Validity.Validity a, GHC.Show.Show i, GHC.Show.Show a, GHC.Classes.Ord i, GHC.Classes.Ord a) => Data.Validity.Validity (Data.Mergeless.Collection.ClientStore i a)
- Data.Mergeless.Collection: instance (Data.Validity.Validity i, Data.Validity.Validity a, GHC.Show.Show i, GHC.Show.Show a, GHC.Classes.Ord i, GHC.Classes.Ord a) => Data.Validity.Validity (Data.Mergeless.Collection.ServerStore i a)
- Data.Mergeless.Collection: instance (Data.Validity.Validity i, Data.Validity.Validity a, GHC.Show.Show i, GHC.Show.Show a, GHC.Classes.Ord i, GHC.Classes.Ord a) => Data.Validity.Validity (Data.Mergeless.Collection.SyncResponse i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Eq a, GHC.Classes.Eq i) => GHC.Classes.Eq (Data.Mergeless.Collection.ClientStore i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Eq a, GHC.Classes.Eq i) => GHC.Classes.Eq (Data.Mergeless.Collection.SyncRequest i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Eq i, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.Mergeless.Collection.ServerStore i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Eq i, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.Mergeless.Collection.SyncResponse i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Ord a, GHC.Classes.Ord i) => GHC.Classes.Ord (Data.Mergeless.Collection.ClientStore i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Ord a, GHC.Classes.Ord i) => GHC.Classes.Ord (Data.Mergeless.Collection.SyncRequest i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Ord i, Data.Aeson.Types.FromJSON.FromJSON i, Data.Aeson.Types.FromJSON.FromJSONKey i, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Data.Mergeless.Collection.ClientStore i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Ord i, Data.Aeson.Types.FromJSON.FromJSON i, Data.Aeson.Types.FromJSON.FromJSONKey i, GHC.Classes.Ord a, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Data.Mergeless.Collection.SyncResponse i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Ord i, Data.Aeson.Types.ToJSON.ToJSON i, Data.Aeson.Types.ToJSON.ToJSONKey i, Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (Data.Mergeless.Collection.ClientStore i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Ord i, GHC.Classes.Ord a) => GHC.Classes.Ord (Data.Mergeless.Collection.ServerStore i a)
- Data.Mergeless.Collection: instance (GHC.Classes.Ord i, GHC.Classes.Ord a) => GHC.Classes.Ord (Data.Mergeless.Collection.SyncResponse i a)
- Data.Mergeless.Collection: instance (GHC.Show.Show a, GHC.Show.Show i) => GHC.Show.Show (Data.Mergeless.Collection.ClientStore i a)
- Data.Mergeless.Collection: instance (GHC.Show.Show a, GHC.Show.Show i) => GHC.Show.Show (Data.Mergeless.Collection.SyncRequest i a)
- Data.Mergeless.Collection: instance (GHC.Show.Show i, GHC.Show.Show a) => GHC.Show.Show (Data.Mergeless.Collection.ServerStore i a)
- Data.Mergeless.Collection: instance (GHC.Show.Show i, GHC.Show.Show a) => GHC.Show.Show (Data.Mergeless.Collection.SyncResponse i a)
- Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.ClientStore i a)
- Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.ServerStore i a)
- Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.ServerSyncProcessor i a m)
- Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.SyncRequest i a)
- Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.SyncResponse i a)
+ Data.Mergeless.Collection: ClientSyncProcessor :: (Map si a -> m ()) -> (Map ci si -> m ()) -> (Set si -> m ()) -> (Set si -> m ()) -> ClientSyncProcessor ci si a m
+ Data.Mergeless.Collection: [clientSyncProcessorSyncClientAdded] :: ClientSyncProcessor ci si a m -> Map ci si -> m ()
+ Data.Mergeless.Collection: [clientSyncProcessorSyncClientDeleted] :: ClientSyncProcessor ci si a m -> Set si -> m ()
+ Data.Mergeless.Collection: [clientSyncProcessorSyncServerAdded] :: ClientSyncProcessor ci si a m -> Map si a -> m ()
+ Data.Mergeless.Collection: [clientSyncProcessorSyncServerDeleted] :: ClientSyncProcessor ci si a m -> Set si -> m ()
+ Data.Mergeless.Collection: [serverSyncProcessorAddItems] :: ServerSyncProcessor ci si a m -> Map ci a -> m (Map ci si)
+ Data.Mergeless.Collection: [serverSyncProcessorDeleteItems] :: ServerSyncProcessor ci si a m -> Set si -> m (Set si)
+ Data.Mergeless.Collection: [serverSyncProcessorRead] :: ServerSyncProcessor ci si a m -> m (Map si a)
+ Data.Mergeless.Collection: data ClientSyncProcessor ci si a m
+ Data.Mergeless.Collection: emptySyncRequest :: SyncRequest ci si a
+ Data.Mergeless.Collection: instance (Control.DeepSeq.NFData ci, Control.DeepSeq.NFData si, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.Mergeless.Collection.ClientStore ci si a)
+ Data.Mergeless.Collection: instance (Control.DeepSeq.NFData ci, Control.DeepSeq.NFData si, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.Mergeless.Collection.SyncRequest ci si a)
+ Data.Mergeless.Collection: instance (Control.DeepSeq.NFData ci, Control.DeepSeq.NFData si, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.Mergeless.Collection.SyncResponse ci si a)
+ Data.Mergeless.Collection: instance (Control.DeepSeq.NFData si, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.Mergeless.Collection.ServerStore si a)
+ Data.Mergeless.Collection: instance (Data.Aeson.Types.FromJSON.FromJSON ci, Data.Aeson.Types.FromJSON.FromJSON si, Data.Aeson.Types.FromJSON.FromJSON a, Data.Aeson.Types.FromJSON.FromJSONKey ci, GHC.Classes.Ord ci, GHC.Classes.Ord si, GHC.Classes.Ord a) => Data.Aeson.Types.FromJSON.FromJSON (Data.Mergeless.Collection.SyncRequest ci si a)
+ Data.Mergeless.Collection: instance (Data.Aeson.Types.FromJSON.FromJSONKey si, GHC.Classes.Ord si, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Data.Mergeless.Collection.ServerStore si a)
+ Data.Mergeless.Collection: instance (Data.Aeson.Types.ToJSON.ToJSON a, Data.Aeson.Types.ToJSON.ToJSONKey si) => Data.Aeson.Types.ToJSON.ToJSON (Data.Mergeless.Collection.ServerStore si a)
+ Data.Mergeless.Collection: instance (Data.Aeson.Types.ToJSON.ToJSON ci, Data.Aeson.Types.ToJSON.ToJSON si, Data.Aeson.Types.ToJSON.ToJSON a, Data.Aeson.Types.ToJSON.ToJSONKey ci) => Data.Aeson.Types.ToJSON.ToJSON (Data.Mergeless.Collection.SyncRequest ci si a)
+ Data.Mergeless.Collection: instance (Data.Aeson.Types.ToJSON.ToJSON ci, Data.Aeson.Types.ToJSON.ToJSON si, Data.Aeson.Types.ToJSON.ToJSONKey ci, Data.Aeson.Types.ToJSON.ToJSONKey si, Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (Data.Mergeless.Collection.SyncResponse ci si a)
+ Data.Mergeless.Collection: instance (Data.Validity.Validity ci, Data.Validity.Validity si, Data.Validity.Validity a, GHC.Classes.Ord ci, GHC.Classes.Ord si, GHC.Show.Show ci) => Data.Validity.Validity (Data.Mergeless.Collection.SyncRequest ci si a)
+ Data.Mergeless.Collection: instance (Data.Validity.Validity ci, Data.Validity.Validity si, Data.Validity.Validity a, GHC.Show.Show ci, GHC.Show.Show si, GHC.Classes.Ord ci, GHC.Classes.Ord si) => Data.Validity.Validity (Data.Mergeless.Collection.ClientStore ci si a)
+ Data.Mergeless.Collection: instance (Data.Validity.Validity ci, Data.Validity.Validity si, Data.Validity.Validity a, GHC.Show.Show ci, GHC.Show.Show si, GHC.Classes.Ord ci, GHC.Classes.Ord si) => Data.Validity.Validity (Data.Mergeless.Collection.SyncResponse ci si a)
+ Data.Mergeless.Collection: instance (Data.Validity.Validity si, Data.Validity.Validity a, GHC.Show.Show si, GHC.Show.Show a, GHC.Classes.Ord si) => Data.Validity.Validity (Data.Mergeless.Collection.ServerStore si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Eq ci, GHC.Classes.Eq a, GHC.Classes.Eq si) => GHC.Classes.Eq (Data.Mergeless.Collection.ClientStore ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Eq ci, GHC.Classes.Eq a, GHC.Classes.Eq si) => GHC.Classes.Eq (Data.Mergeless.Collection.SyncRequest ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Eq ci, GHC.Classes.Eq si, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.Mergeless.Collection.SyncResponse ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Eq si, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.Mergeless.Collection.ServerStore si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Ord ci, Data.Aeson.Types.FromJSON.FromJSON ci, Data.Aeson.Types.FromJSON.FromJSONKey ci, GHC.Classes.Ord si, Data.Aeson.Types.FromJSON.FromJSON si, Data.Aeson.Types.FromJSON.FromJSONKey si, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Data.Mergeless.Collection.ClientStore ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Ord ci, Data.Aeson.Types.ToJSON.ToJSON ci, Data.Aeson.Types.ToJSON.ToJSONKey ci, GHC.Classes.Ord si, Data.Aeson.Types.ToJSON.ToJSON si, Data.Aeson.Types.ToJSON.ToJSONKey si, Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (Data.Mergeless.Collection.ClientStore ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Ord ci, GHC.Classes.Ord a, GHC.Classes.Ord si) => GHC.Classes.Ord (Data.Mergeless.Collection.ClientStore ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Ord ci, GHC.Classes.Ord a, GHC.Classes.Ord si) => GHC.Classes.Ord (Data.Mergeless.Collection.SyncRequest ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Ord ci, GHC.Classes.Ord si, Data.Aeson.Types.FromJSON.FromJSON ci, Data.Aeson.Types.FromJSON.FromJSON si, Data.Aeson.Types.FromJSON.FromJSONKey ci, Data.Aeson.Types.FromJSON.FromJSONKey si, GHC.Classes.Ord a, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Data.Mergeless.Collection.SyncResponse ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Ord ci, GHC.Classes.Ord si, GHC.Classes.Ord a) => GHC.Classes.Ord (Data.Mergeless.Collection.SyncResponse ci si a)
+ Data.Mergeless.Collection: instance (GHC.Classes.Ord si, GHC.Classes.Ord a) => GHC.Classes.Ord (Data.Mergeless.Collection.ServerStore si a)
+ Data.Mergeless.Collection: instance (GHC.Show.Show ci, GHC.Show.Show a, GHC.Show.Show si) => GHC.Show.Show (Data.Mergeless.Collection.ClientStore ci si a)
+ Data.Mergeless.Collection: instance (GHC.Show.Show ci, GHC.Show.Show a, GHC.Show.Show si) => GHC.Show.Show (Data.Mergeless.Collection.SyncRequest ci si a)
+ Data.Mergeless.Collection: instance (GHC.Show.Show ci, GHC.Show.Show si, GHC.Show.Show a) => GHC.Show.Show (Data.Mergeless.Collection.SyncResponse ci si a)
+ Data.Mergeless.Collection: instance (GHC.Show.Show si, GHC.Show.Show a) => GHC.Show.Show (Data.Mergeless.Collection.ServerStore si a)
+ Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.ClientStore ci si a)
+ Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.ClientSyncProcessor ci si a m)
+ Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.ServerStore si a)
+ Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.ServerSyncProcessor ci si a m)
+ Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.SyncRequest ci si a)
+ Data.Mergeless.Collection: instance GHC.Generics.Generic (Data.Mergeless.Collection.SyncResponse ci si a)
+ Data.Mergeless.Collection: mergeSyncResponseCustom :: Monad m => ClientSyncProcessor ci si a m -> SyncResponse ci si a -> m ()
+ Data.Mergeless.Collection: pureClientSyncProcessor :: forall ci si a. (Ord ci, Ord si) => ClientSyncProcessor ci si a (State (ClientStore ci si a))
- Data.Mergeless.Collection: ClientStore :: !Map ClientId a -> !Map i a -> !Set i -> ClientStore i a
+ Data.Mergeless.Collection: ClientStore :: !Map ci a -> !Map si a -> !Set si -> ClientStore ci si a
- Data.Mergeless.Collection: ServerStore :: Map i a -> ServerStore i a
+ Data.Mergeless.Collection: ServerStore :: Map si a -> ServerStore si a
- Data.Mergeless.Collection: ServerSyncProcessor :: (Set i -> m (Set i)) -> (Set i -> m (Set i)) -> (Set i -> m (Map i a)) -> (Map ClientId a -> m (Map ClientId i)) -> ServerSyncProcessor i a m
+ Data.Mergeless.Collection: ServerSyncProcessor :: m (Map si a) -> (Map ci a -> m (Map ci si)) -> (Set si -> m (Set si)) -> ServerSyncProcessor ci si a m
- Data.Mergeless.Collection: SyncRequest :: !Map ClientId a -> !Set i -> !Set i -> SyncRequest i a
+ Data.Mergeless.Collection: SyncRequest :: !Map ci a -> !Set si -> !Set si -> SyncRequest ci si a
- Data.Mergeless.Collection: SyncResponse :: !Map ClientId i -> !Set i -> !Map i a -> !Set i -> SyncResponse i a
+ Data.Mergeless.Collection: SyncResponse :: !Map ci si -> !Set si -> !Map si a -> !Set si -> SyncResponse ci si a
- Data.Mergeless.Collection: [clientStoreAdded] :: ClientStore i a -> !Map ClientId a
+ Data.Mergeless.Collection: [clientStoreAdded] :: ClientStore ci si a -> !Map ci a
- Data.Mergeless.Collection: [clientStoreDeleted] :: ClientStore i a -> !Set i
+ Data.Mergeless.Collection: [clientStoreDeleted] :: ClientStore ci si a -> !Set si
- Data.Mergeless.Collection: [clientStoreSynced] :: ClientStore i a -> !Map i a
+ Data.Mergeless.Collection: [clientStoreSynced] :: ClientStore ci si a -> !Map si a
- Data.Mergeless.Collection: [serverStoreItems] :: ServerStore i a -> Map i a
+ Data.Mergeless.Collection: [serverStoreItems] :: ServerStore si a -> Map si a
- Data.Mergeless.Collection: [syncRequestAdded] :: SyncRequest i a -> !Map ClientId a
+ Data.Mergeless.Collection: [syncRequestAdded] :: SyncRequest ci si a -> !Map ci a
- Data.Mergeless.Collection: [syncRequestDeleted] :: SyncRequest i a -> !Set i
+ Data.Mergeless.Collection: [syncRequestDeleted] :: SyncRequest ci si a -> !Set si
- Data.Mergeless.Collection: [syncRequestSynced] :: SyncRequest i a -> !Set i
+ Data.Mergeless.Collection: [syncRequestSynced] :: SyncRequest ci si a -> !Set si
- Data.Mergeless.Collection: [syncResponseClientAdded] :: SyncResponse i a -> !Map ClientId i
+ Data.Mergeless.Collection: [syncResponseClientAdded] :: SyncResponse ci si a -> !Map ci si
- Data.Mergeless.Collection: [syncResponseClientDeleted] :: SyncResponse i a -> !Set i
+ Data.Mergeless.Collection: [syncResponseClientDeleted] :: SyncResponse ci si a -> !Set si
- Data.Mergeless.Collection: [syncResponseServerAdded] :: SyncResponse i a -> !Map i a
+ Data.Mergeless.Collection: [syncResponseServerAdded] :: SyncResponse ci si a -> !Map si a
- Data.Mergeless.Collection: [syncResponseServerDeleted] :: SyncResponse i a -> !Set i
+ Data.Mergeless.Collection: [syncResponseServerDeleted] :: SyncResponse ci si a -> !Set si
- Data.Mergeless.Collection: addItemToClientStore :: (Ord i, Ord a) => a -> ClientStore i a -> ClientStore i a
+ Data.Mergeless.Collection: addItemToClientStore :: (Enum ci, Bounded ci, Ord ci) => a -> ClientStore ci si a -> ClientStore ci si a
- Data.Mergeless.Collection: data ClientStore i a
+ Data.Mergeless.Collection: data ClientStore ci si a
- Data.Mergeless.Collection: data ServerSyncProcessor i a m
+ Data.Mergeless.Collection: data ServerSyncProcessor ci si a m
- Data.Mergeless.Collection: data SyncRequest i a
+ Data.Mergeless.Collection: data SyncRequest ci si a
- Data.Mergeless.Collection: data SyncResponse i a
+ Data.Mergeless.Collection: data SyncResponse ci si a
- Data.Mergeless.Collection: deleteSyncedFromClientStore :: (Ord i, Ord a) => i -> ClientStore i a -> ClientStore i a
+ Data.Mergeless.Collection: deleteSyncedFromClientStore :: Ord si => si -> ClientStore ci si a -> ClientStore ci si a
- Data.Mergeless.Collection: deleteUnsyncedFromClientStore :: (Ord i, Ord a) => ClientId -> ClientStore i a -> ClientStore i a
+ Data.Mergeless.Collection: deleteUnsyncedFromClientStore :: Ord ci => ci -> ClientStore ci si a -> ClientStore ci si a
- Data.Mergeless.Collection: emptyClientStore :: ClientStore i a
+ Data.Mergeless.Collection: emptyClientStore :: ClientStore ci si a
- Data.Mergeless.Collection: emptyServerStore :: ServerStore i a
+ Data.Mergeless.Collection: emptyServerStore :: ServerStore si a
- Data.Mergeless.Collection: emptySyncResponse :: SyncResponse i ia
+ Data.Mergeless.Collection: emptySyncResponse :: SyncResponse ci si a
- Data.Mergeless.Collection: makeSyncRequest :: (Ord i, Ord a) => ClientStore i a -> SyncRequest i a
+ Data.Mergeless.Collection: makeSyncRequest :: ClientStore ci si a -> SyncRequest ci si a
- Data.Mergeless.Collection: mergeSyncResponse :: forall i a. (Ord i, Ord a) => ClientStore i a -> SyncResponse i a -> ClientStore i a
+ Data.Mergeless.Collection: mergeSyncResponse :: forall ci si a. (Ord ci, Ord si) => ClientStore ci si a -> SyncResponse ci si a -> ClientStore ci si a
- Data.Mergeless.Collection: newtype ServerStore i a
+ Data.Mergeless.Collection: newtype ServerStore si a
- Data.Mergeless.Collection: processServerSync :: forall m i a. (Ord i, Ord a, Monad m) => m i -> ServerStore i a -> SyncRequest i a -> m (SyncResponse i a, ServerStore i a)
+ Data.Mergeless.Collection: processServerSync :: forall m ci si a. (Ord si, Monad m) => m si -> ServerStore si a -> SyncRequest ci si a -> m (SyncResponse ci si a, ServerStore si a)
- Data.Mergeless.Collection: processServerSyncCustom :: forall i a m. (Ord i, Ord a, Monad m) => ServerSyncProcessor i a m -> SyncRequest i a -> m (SyncResponse i a)
+ Data.Mergeless.Collection: processServerSyncCustom :: forall ci si a m. (Ord si, Monad m) => ServerSyncProcessor ci si a m -> SyncRequest ci si a -> m (SyncResponse ci si a)
- Data.Mergeless.Collection: storeSize :: ClientStore i a -> Int
+ Data.Mergeless.Collection: storeSize :: ClientStore ci si a -> Int

Files

ChangeLog.md view
@@ -2,6 +2,18 @@  ## Unreleased changes +## [0.3.0.0] - 2020-05-21++### Added++* mergeSyncResponseCustom+* emptySyncResponse: in case you want to do the sync response creation yourself++### Changed++* Another parameter: the ClientId, so that you can use an sqlid as your client id+  This likely means that none of your code from mergeless-0.2 still compiles but it's easy to fix.+ ## [0.2.0.1] - 2020-02-13  * Fixed the benchmarks
mergeless.cabal view
@@ -4,16 +4,16 @@ -- -- see: https://github.com/sol/hpack ----- hash: 1f6863bf51a407e32135695c2be4fd727c6474cfe30e5543ca7974cf89827b54+-- hash: 5f5e451a78c312ccbef6cc567464e0983bc9a8fbba6a7f428016c9f186aeebcb  name:           mergeless-version:        0.2.0.2+version:        0.3.0.0 description:    Please see the README on GitHub at <https://github.com/NorfairKing/mergeless#readme> homepage:       https://github.com/NorfairKing/mergeless#readme bug-reports:    https://github.com/NorfairKing/mergeless/issues author:         Tom Sydney Kerckhove maintainer:     syd.kerckhove@gmail.com-copyright:      Copyright: (c) 2018 Tom Sydney Kerckhove+copyright:      Copyright: (c) 2018-2020 Tom Sydney Kerckhove license:        MIT build-type:     Simple extra-source-files:@@ -33,6 +33,7 @@       Paths_mergeless   hs-source-dirs:       src+  ghc-options: -Wall -fwarn-redundant-constraints   build-depends:       aeson     , base >=4.11 && <5
src/Data/Mergeless.hs view
@@ -1,5 +1,6 @@ module Data.Mergeless-  ( module Data.Mergeless.Collection-  ) where+  ( module Data.Mergeless.Collection,+  )+where  import Data.Mergeless.Collection
src/Data/Mergeless/Collection.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}@@ -11,9 +12,10 @@ -- -- * Items must be immutable. -- * Items must allow for a centrally unique identifier.--- * Identifiers for items must be generatable in such a way that they are certainly unique.+-- * Items must allow for a client-side unique identifier.+-- * Identifiers for items must be generated in such a way that they are certainly unique. ----- Should mutation be a requirement, then it can be build such that it entails deleting the old version and creating a new version that is the modification of the old version.+-- Should mutation be a requirement, then there is another library: 'mergeful' for exactly this purpose. -- -- -- There are a few obvious candidates for identifiers:@@ -42,113 +44,125 @@ -- * The client sends that request to the central server and gets a 'SyncResponse'. -- * The client then updates its local store with 'mergeSyncResponse'. module Data.Mergeless.Collection-  ( ClientId(..)-  , ClientStore(..)-  , emptyClientStore-  , storeSize-  , addItemToClientStore-  , deleteUnsyncedFromClientStore-  , deleteSyncedFromClientStore-  , SyncRequest(..)-  , SyncResponse(..)-  , emptySyncResponse+  ( ClientStore (..),+    SyncRequest (..),+    SyncResponse (..),+     -- * Client-side Synchronisation-  , makeSyncRequest-  , mergeSyncResponse-  , addRemotelyAddedItems-  , addAddedItems-  , deleteItemsToBeDeletedLocally-  , deleteLocalUndeletedItems++    -- ** General+    ClientSyncProcessor (..),+    mergeSyncResponseCustom,++    -- ** Pure+    emptyClientStore,+    ClientId (..),+    storeSize,+    addItemToClientStore,+    deleteUnsyncedFromClientStore,+    deleteSyncedFromClientStore,+    emptySyncRequest,+    makeSyncRequest,+    mergeSyncResponse,+    pureClientSyncProcessor,+     -- * Server-side Synchronisation-    -- ** General synchronisation-  , ServerSyncProcessor(..)-  , processServerSyncCustom-    -- ** Synchronisation with a simple central store-  , ServerStore(..)-  , emptyServerStore-  , processServerSync-  ) where -import GHC.Generics (Generic)+    -- ** General synchronisation+    ServerSyncProcessor (..),+    processServerSyncCustom, -import Data.Validity-import Data.Validity.Containers ()+    -- ** Synchronisation with a simple central store+    ServerStore (..),+    emptyServerStore,+    emptySyncResponse,+    processServerSync,+  )+where +import Control.DeepSeq+import Control.Monad.State.Strict import Data.Aeson import Data.List import Data.Map.Strict (Map) import qualified Data.Map.Strict as M import qualified Data.Set as S import Data.Set (Set)+import Data.Validity+import Data.Validity.Containers () import Data.Word--import Control.DeepSeq-import Control.Monad.State.Strict+import GHC.Generics (Generic)  {-# ANN module ("HLint: ignore Use lambda-case" :: String) #-} --- | A Client-side identifier for items.+-- | A Client-side identifier for items for use with pure client stores -- -- These only need to be unique at the client.-newtype ClientId =-  ClientId-    { unClientId :: Word64-    }+newtype ClientId+  = ClientId+      { unClientId :: Word64+      }   deriving (Show, Eq, Ord, Enum, Bounded, Generic, ToJSON, ToJSONKey, FromJSON, FromJSONKey)  instance Validity ClientId  instance NFData ClientId --- | A client-side store of items with Id's of type @i@ and values of type @a@-data ClientStore i a =-  ClientStore-    { clientStoreAdded :: !(Map ClientId a)-    , clientStoreSynced :: !(Map i a)-    , clientStoreDeleted :: !(Set i)-    }+-- | A client-side store of items with Client Id's of type @ci@, Server Id's of type @i@ and values of type @a@+data ClientStore ci si a+  = ClientStore+      { clientStoreAdded :: !(Map ci a),+        clientStoreSynced :: !(Map si a),+        clientStoreDeleted :: !(Set si)+      }   deriving (Show, Eq, Ord, Generic) -instance (NFData i, NFData a) => NFData (ClientStore i a)+instance (NFData ci, NFData si, NFData a) => NFData (ClientStore ci si a) -instance (Validity i, Validity a, Show i, Show a, Ord i, Ord a) => Validity (ClientStore i a) where+instance (Validity ci, Validity si, Validity a, Show ci, Show si, Ord ci, Ord si) => Validity (ClientStore ci si a) where   validate cs@ClientStore {..} =     mconcat-      [ genericValidate cs-      , declare "the store items have distinct ids" $-        distinct $ M.keys clientStoreSynced ++ S.toList clientStoreDeleted+      [ genericValidate cs,+        declare "the store items have distinct ids"+          $ distinct+          $ M.keys clientStoreSynced ++ S.toList clientStoreDeleted       ] -instance (Ord i, FromJSON i, FromJSONKey i, FromJSON a) => FromJSON (ClientStore i a) where+instance (Ord ci, FromJSON ci, FromJSONKey ci, Ord si, FromJSON si, FromJSONKey si, FromJSON a) => FromJSON (ClientStore ci si a) where   parseJSON =     withObject "ClientStore" $ \o ->-      ClientStore <$> o .:? "added" .!= M.empty <*> o .:? "synced" .!= M.empty <*>-      o .:? "deleted" .!= S.empty+      ClientStore <$> o .:? "added" .!= M.empty <*> o .:? "synced" .!= M.empty+        <*> o .:? "deleted" .!= S.empty -instance (Ord i, ToJSON i, ToJSONKey i, ToJSON a) => ToJSON (ClientStore i a) where+instance (Ord ci, ToJSON ci, ToJSONKey ci, Ord si, ToJSON si, ToJSONKey si, ToJSON a) => ToJSON (ClientStore ci si a) where   toJSON ClientStore {..} =     object       ["added" .= clientStoreAdded, "synced" .= clientStoreSynced, "deleted" .= clientStoreDeleted] --- | The store with no items.-emptyClientStore :: ClientStore i a+-- | The client store with no items.+emptyClientStore :: ClientStore ci si a emptyClientStore =   ClientStore-    {clientStoreAdded = M.empty, clientStoreSynced = M.empty, clientStoreDeleted = S.empty}+    { clientStoreAdded = M.empty,+      clientStoreSynced = M.empty,+      clientStoreDeleted = S.empty+    }  -- | The number of items in a store -- -- This does not count the deleted items, so that those really look deleted.-storeSize :: ClientStore i a -> Int+storeSize :: ClientStore ci si a -> Int storeSize ClientStore {..} = M.size clientStoreAdded + M.size clientStoreSynced -clientStoreIds :: Ord i => ClientStore i a -> Set i+clientStoreIds :: Ord si => ClientStore ci si a -> Set si clientStoreIds ClientStore {..} = M.keysSet clientStoreSynced `S.union` clientStoreDeleted  -- | Add an item to a client store as an added item. ----- This will take care of the uniqueness constraint of the 'ClientId's in the map.-addItemToClientStore :: (Ord i, Ord a) => a -> ClientStore i a -> ClientStore i a+-- This will take care of the uniqueness constraint of the 'ci's in the map.+--+-- The values wrap around when reaching 'maxBound'.+addItemToClientStore :: (Enum ci, Bounded ci, Ord ci) => a -> ClientStore ci si a -> ClientStore ci si a addItemToClientStore a cs =   let oldAddedItems = clientStoreAdded cs       newAddedItems =@@ -159,276 +173,269 @@ -- | Find a free client id to use -- -- You shouldn't need this function, 'addItemToClientStore' takes care of this.-findFreeSpot :: Map ClientId a -> ClientId+--+-- The values wrap around when reaching 'maxBound'.+findFreeSpot :: (Ord ci, Enum ci, Bounded ci) => Map ci a -> ci findFreeSpot m =   if M.null m-    then ClientId 0-    else let (i, _) = M.findMax m-          in go (next i)+    then minBound+    else+      let (i, _) = M.findMax m+       in go (next i)   where     go i =       if M.member i m         then go (next i)         else i-    next (ClientId w)-      | w == maxBound = ClientId 0-      | otherwise = ClientId $ succ w+    next ci+      | ci == maxBound = minBound+      | otherwise = succ ci -deleteUnsyncedFromClientStore :: (Ord i, Ord a) => ClientId -> ClientStore i a -> ClientStore i a+deleteUnsyncedFromClientStore :: Ord ci => ci -> ClientStore ci si a -> ClientStore ci si a deleteUnsyncedFromClientStore cid cs = cs {clientStoreAdded = M.delete cid $ clientStoreAdded cs} -deleteSyncedFromClientStore :: (Ord i, Ord a) => i -> ClientStore i a -> ClientStore i a+deleteSyncedFromClientStore :: Ord si => si -> ClientStore ci si a -> ClientStore ci si a deleteSyncedFromClientStore i cs =   let syncedBefore = clientStoreSynced cs    in case M.lookup i syncedBefore of         Nothing -> cs         Just _ ->           cs-            { clientStoreSynced = M.delete i syncedBefore-            , clientStoreDeleted = S.insert i $ clientStoreDeleted cs+            { clientStoreSynced = M.delete i syncedBefore,+              clientStoreDeleted = S.insert i $ clientStoreDeleted cs             } --- | A synchronisation request for items with identifiers of type @i@ and values of type @a@-data SyncRequest i a =-  SyncRequest-    { syncRequestAdded :: !(Map ClientId a)-    , syncRequestSynced :: !(Set i)-    , syncRequestDeleted :: !(Set i)-    }+-- | A synchronisation request for items with Client Id's of type @ci@, Server Id's of type @i@ and values of type @a@+data SyncRequest ci si a+  = SyncRequest+      { syncRequestAdded :: !(Map ci a),+        syncRequestSynced :: !(Set si),+        syncRequestDeleted :: !(Set si)+      }   deriving (Show, Eq, Ord, Generic) -instance (NFData i, NFData a) => NFData (SyncRequest i a)+instance (NFData ci, NFData si, NFData a) => NFData (SyncRequest ci si a) -instance (Validity i, Validity a, Ord i, Ord a) => Validity (SyncRequest i a) where+instance (Validity ci, Validity si, Validity a, Ord ci, Ord si, Show ci) => Validity (SyncRequest ci si a) where   validate sr@SyncRequest {..} =     mconcat-      [ genericValidate sr-      , declare "the sync request items have distinct ids" $-        distinct $ S.toList syncRequestSynced ++ S.toList syncRequestDeleted+      [ genericValidate sr,+        declare "the sync request items have distinct ids"+          $ distinct+          $ S.toList syncRequestSynced ++ S.toList syncRequestDeleted       ] -instance (FromJSON i, FromJSON a, Ord i, Ord a) => FromJSON (SyncRequest i a) where+instance (FromJSON ci, FromJSON si, FromJSON a, FromJSONKey ci, Ord ci, Ord si, Ord a) => FromJSON (SyncRequest ci si a) where   parseJSON =     withObject "SyncRequest" $ \o ->-      SyncRequest <$> o .: "added" <*> o .: "synced" <*> o .: "undeleted"+      SyncRequest <$> o .: "added" <*> o .: "synced" <*> o .: "deleted" -instance (ToJSON i, ToJSON a) => ToJSON (SyncRequest i a) where+instance (ToJSON ci, ToJSON si, ToJSON a, ToJSONKey ci) => ToJSON (SyncRequest ci si a) where   toJSON SyncRequest {..} =     object-      [ "added" .= syncRequestAdded-      , "synced" .= syncRequestSynced-      , "undeleted" .= syncRequestDeleted+      [ "added" .= syncRequestAdded,+        "synced" .= syncRequestSynced,+        "deleted" .= syncRequestDeleted       ] +emptySyncRequest :: SyncRequest ci si a+emptySyncRequest =+  SyncRequest+    { syncRequestAdded = M.empty,+      syncRequestSynced = S.empty,+      syncRequestDeleted = S.empty+    }+ -- | Produce a synchronisation request for a client-side store. -- -- This request can then be sent to a central store for synchronisation.-makeSyncRequest :: (Ord i, Ord a) => ClientStore i a -> SyncRequest i a+makeSyncRequest :: ClientStore ci si a -> SyncRequest ci si a makeSyncRequest ClientStore {..} =   SyncRequest-    { syncRequestAdded = clientStoreAdded-    , syncRequestSynced = M.keysSet clientStoreSynced-    , syncRequestDeleted = clientStoreDeleted+    { syncRequestAdded = clientStoreAdded,+      syncRequestSynced = M.keysSet clientStoreSynced,+      syncRequestDeleted = clientStoreDeleted     }  -- | A synchronisation response for items with identifiers of type @i@ and values of type @a@-data SyncResponse i a =-  SyncResponse-    { syncResponseClientAdded :: !(Map ClientId i)-    , syncResponseClientDeleted :: !(Set i)-    , syncResponseServerAdded :: !(Map i a)-    , syncResponseServerDeleted :: !(Set i)-    }+data SyncResponse ci si a+  = SyncResponse+      { syncResponseClientAdded :: !(Map ci si),+        syncResponseClientDeleted :: !(Set si),+        syncResponseServerAdded :: !(Map si a),+        syncResponseServerDeleted :: !(Set si)+      }   deriving (Show, Eq, Ord, Generic) -instance (NFData i, NFData a) => NFData (SyncResponse i a)+instance (NFData ci, NFData si, NFData a) => NFData (SyncResponse ci si a) -instance (Validity i, Validity a, Show i, Show a, Ord i, Ord a) => Validity (SyncResponse i a) where+instance (Validity ci, Validity si, Validity a, Show ci, Show si, Ord ci, Ord si) => Validity (SyncResponse ci si a) where   validate sr@SyncResponse {..} =     mconcat-      [ genericValidate sr-      , declare "the sync response items have distinct uuids" $-        distinct $-        concat-          [ M.elems syncResponseClientAdded-          , S.toList syncResponseClientDeleted-          , M.keys syncResponseServerAdded-          , S.toList syncResponseServerDeleted-          ]+      [ genericValidate sr,+        declare "the sync response items have distinct uuids"+          $ distinct+          $ concat+            [ M.elems syncResponseClientAdded,+              S.toList syncResponseClientDeleted,+              M.keys syncResponseServerAdded,+              S.toList syncResponseServerDeleted+            ]       ] -instance (Ord i, FromJSON i, FromJSONKey i, Ord a, FromJSON a) => FromJSON (SyncResponse i a) where+instance (Ord ci, Ord si, FromJSON ci, FromJSON si, FromJSONKey ci, FromJSONKey si, Ord a, FromJSON a) => FromJSON (SyncResponse ci si a) where   parseJSON =     withObject "SyncResponse" $ \o ->-      SyncResponse <$> o .: "client-added" <*> o .: "client-deleted" <*> o .: "server-added" <*>-      o .: "server-deleted"+      SyncResponse <$> o .: "client-added" <*> o .: "client-deleted" <*> o .: "server-added"+        <*> o+        .: "server-deleted" -instance (ToJSON i, ToJSONKey i, ToJSON a) => ToJSON (SyncResponse i a) where+instance (ToJSON ci, ToJSON si, ToJSONKey ci, ToJSONKey si, ToJSON a) => ToJSON (SyncResponse ci si a) where   toJSON SyncResponse {..} =     object-      [ "client-added" .= syncResponseClientAdded-      , "client-deleted" .= syncResponseClientDeleted-      , "server-added" .= syncResponseServerAdded-      , "server-deleted" .= syncResponseServerDeleted+      [ "client-added" .= syncResponseClientAdded,+        "client-deleted" .= syncResponseClientDeleted,+        "server-added" .= syncResponseServerAdded,+        "server-deleted" .= syncResponseServerDeleted       ] -emptySyncResponse :: SyncResponse i ia+emptySyncResponse :: SyncResponse ci si a emptySyncResponse =   SyncResponse-    { syncResponseClientAdded = M.empty-    , syncResponseClientDeleted = S.empty-    , syncResponseServerAdded = M.empty-    , syncResponseServerDeleted = S.empty+    { syncResponseClientAdded = M.empty,+      syncResponseClientDeleted = S.empty,+      syncResponseServerAdded = M.empty,+      syncResponseServerDeleted = S.empty     }  -- | Merge a synchronisation response back into a client-side store. mergeSyncResponse ::-     forall i a. (Ord i, Ord a)-  => ClientStore i a-  -> SyncResponse i a-  -> ClientStore i a-mergeSyncResponse s SyncResponse {..} =-  addRemotelyAddedItems syncResponseServerAdded .-  addAddedItems syncResponseClientAdded .-  deleteItemsToBeDeletedLocally syncResponseServerDeleted .-  deleteLocalUndeletedItems syncResponseClientDeleted $-  s--addRemotelyAddedItems :: (Ord i, Ord a) => Map i a -> ClientStore i a -> ClientStore i a-addRemotelyAddedItems m cs =-  cs {clientStoreSynced = M.union (clientStoreSynced cs) (m `diffSet` clientStoreIds cs)}+  forall ci si a.+  (Ord ci, Ord si) =>+  ClientStore ci si a ->+  SyncResponse ci si a ->+  ClientStore ci si a+mergeSyncResponse s sr =+  flip execState s $+    mergeSyncResponseCustom+      pureClientSyncProcessor+      sr -addAddedItems ::-     forall i a. (Ord i, Ord a)-  => Map ClientId i-  -> ClientStore i a-  -> ClientStore i a-addAddedItems addedItems cs =-  let oldAdded = clientStoreAdded cs-      oldSynced = clientStoreSynced cs-      go :: (Map ClientId a, Map i a) -> ClientId -> i -> (Map ClientId a, Map i a)-      go (added, synced) cid i =-        case M.lookup cid added of-          Nothing -> (added, synced)-          Just a -> (M.delete cid added, M.insert i a synced)-      (newAdded, newSynced) = M.foldlWithKey go (oldAdded, oldSynced) addedItems-   in cs {clientStoreAdded = newAdded, clientStoreSynced = newSynced}+pureClientSyncProcessor :: forall ci si a. (Ord ci, Ord si) => ClientSyncProcessor ci si a (State (ClientStore ci si a))+pureClientSyncProcessor =+  ClientSyncProcessor+    { clientSyncProcessorSyncServerAdded = \m -> modify $ \cs ->+        cs {clientStoreSynced = M.union (clientStoreSynced cs) (m `diffSet` clientStoreIds cs)},+      clientSyncProcessorSyncClientAdded = \addedItems -> modify $ \cs ->+        let oldAdded = clientStoreAdded cs+            oldSynced = clientStoreSynced cs+            go :: (Map ci a, Map si a) -> ci -> si -> (Map ci a, Map si a)+            go (added, synced) cid i =+              case M.lookup cid added of+                Nothing -> (added, synced)+                Just a -> (M.delete cid added, M.insert i a synced)+            (newAdded, newSynced) = M.foldlWithKey go (oldAdded, oldSynced) addedItems+         in cs {clientStoreAdded = newAdded, clientStoreSynced = newSynced},+      clientSyncProcessorSyncServerDeleted = \toBeDeletedLocally -> modify $ \cs ->+        cs {clientStoreSynced = clientStoreSynced cs `diffSet` toBeDeletedLocally},+      clientSyncProcessorSyncClientDeleted = \cd -> modify $ \cs ->+        cs {clientStoreDeleted = clientStoreDeleted cs `S.difference` cd}+    } -deleteItemsToBeDeletedLocally :: (Ord i, Ord a) => Set i -> ClientStore i a -> ClientStore i a-deleteItemsToBeDeletedLocally toBeDeletedLocally cs =-  cs {clientStoreSynced = clientStoreSynced cs `diffSet` toBeDeletedLocally}+data ClientSyncProcessor ci si a m+  = ClientSyncProcessor+      { clientSyncProcessorSyncServerAdded :: Map si a -> m (),+        clientSyncProcessorSyncClientAdded :: Map ci si -> m (),+        clientSyncProcessorSyncServerDeleted :: Set si -> m (),+        clientSyncProcessorSyncClientDeleted :: Set si -> m ()+      }+  deriving (Generic) -deleteLocalUndeletedItems :: (Ord i, Ord a) => Set i -> ClientStore i a -> ClientStore i a-deleteLocalUndeletedItems cd cs = cs {clientStoreDeleted = clientStoreDeleted cs `S.difference` cd}+mergeSyncResponseCustom :: Monad m => ClientSyncProcessor ci si a m -> SyncResponse ci si a -> m ()+mergeSyncResponseCustom ClientSyncProcessor {..} SyncResponse {..} = do+  -- The order here matters!+  clientSyncProcessorSyncServerAdded syncResponseServerAdded+  clientSyncProcessorSyncServerDeleted syncResponseServerDeleted+  clientSyncProcessorSyncClientDeleted syncResponseClientDeleted+  clientSyncProcessorSyncClientAdded syncResponseClientAdded  -- | A record of the basic operations that are necessary to build a synchronisation processor.-data ServerSyncProcessor i a m =-  ServerSyncProcessor-    { serverSyncProcessorDeleteMany :: Set i -> m (Set i)-      -- ^ Delete the items with an identifier in the given set, return the set that was indeed deleted or did not exist.-      -- In particular, return the identifiers of the items that the client should forget about.-    , serverSyncProcessorQueryNoLongerSynced :: Set i -> m (Set i) -- ^ Query the identifiers of the items that are in the given set but not in the store.-    , serverSyncProcessorQueryNewRemote :: Set i -> m (Map i a) -- ^ Query the items that are in store, but not in the given set.-    , serverSyncProcessorInsertMany :: Map ClientId a -> m (Map ClientId i) -- ^ Insert a set of items into the store.-    }+data ServerSyncProcessor ci si a m+  = ServerSyncProcessor+      { serverSyncProcessorRead :: m (Map si a),+        serverSyncProcessorAddItems :: Map ci a -> m (Map ci si),+        serverSyncProcessorDeleteItems :: Set si -> m (Set si)+      }   deriving (Generic) --- | Process a server-side synchronisation request using a custom synchronisation processor------ WARNING: The identifier generation function must produce newly unique identifiers such that each new item gets a unique identifier.------ You can use this function with deterministically-random identifiers or incrementing identifiers. processServerSyncCustom ::-     forall i a m. (Ord i, Ord a, Monad m)-  => ServerSyncProcessor i a m-  -> SyncRequest i a-  -> m (SyncResponse i a)+  forall ci si a m.+  (Ord si, Monad m) =>+  ServerSyncProcessor ci si a m ->+  SyncRequest ci si a ->+  m (SyncResponse ci si a) processServerSyncCustom ServerSyncProcessor {..} SyncRequest {..} = do-  deletedFromClient <- deleteUndeleted-        -- First we delete the items that were deleted locally but not yet remotely.-        -- Then we find the items that have been deleted remotely but not locally-  deletedRemotely <- syncItemsToBeDeletedLocally-        -- Then we find the items that have appeared remotely but aren't known locally-  newRemoteItems <- syncNewRemoteItems-        -- Then we add the items that should be added.-  newLocalItems <- syncAddedItems-  pure-    SyncResponse-      { syncResponseClientAdded = newLocalItems-      , syncResponseClientDeleted = deletedFromClient-      , syncResponseServerAdded = newRemoteItems-      , syncResponseServerDeleted = deletedRemotely-      }-  where-    deleteUndeleted :: m (Set i)-    deleteUndeleted = serverSyncProcessorDeleteMany syncRequestDeleted-    syncItemsToBeDeletedLocally :: m (Set i)-    syncItemsToBeDeletedLocally = serverSyncProcessorQueryNoLongerSynced syncRequestSynced-    syncNewRemoteItems :: m (Map i a)-    syncNewRemoteItems = serverSyncProcessorQueryNewRemote syncRequestSynced-    syncAddedItems :: m (Map ClientId i)-    syncAddedItems = serverSyncProcessorInsertMany syncRequestAdded+  serverItems <- serverSyncProcessorRead+  let syncResponseServerAdded = serverItems `M.difference` toMap (syncRequestSynced `S.union` syncRequestDeleted)+  let syncResponseServerDeleted = syncRequestSynced `S.difference` M.keysSet serverItems+  syncResponseClientDeleted <- serverSyncProcessorDeleteItems syncRequestDeleted+  syncResponseClientAdded <- serverSyncProcessorAddItems syncRequestAdded+  pure SyncResponse {..}  -- | A central store of items with identifiers of type @i@ and values of type @a@-newtype ServerStore i a =-  ServerStore-    { serverStoreItems :: Map i a-    }+newtype ServerStore si a+  = ServerStore+      { serverStoreItems :: Map si a+      }   deriving (Show, Eq, Ord, Generic, FromJSON, ToJSON) -instance (NFData i, NFData a) => NFData (ServerStore i a)+instance (NFData si, NFData a) => NFData (ServerStore si a) -instance (Validity i, Validity a, Show i, Show a, Ord i, Ord a) => Validity (ServerStore i a)+instance (Validity si, Validity a, Show si, Show a, Ord si) => Validity (ServerStore si a)  -- | An empty central store to start with-emptyServerStore :: ServerStore i a+emptyServerStore :: ServerStore si a emptyServerStore = ServerStore {serverStoreItems = M.empty} --- | Process a server-side synchronisation request using @getCurrentTime@+-- | Process a server-side synchronisation request using a server id generator -- -- see 'processSyncCustom' processServerSync ::-     forall m i a. (Ord i, Ord a, Monad m)-  => m i-  -> ServerStore i a-  -> SyncRequest i a-  -> m (SyncResponse i a, ServerStore i a)+  forall m ci si a.+  (Ord si, Monad m) =>+  m si ->+  ServerStore si a ->+  SyncRequest ci si a ->+  m (SyncResponse ci si a, ServerStore si a) processServerSync genUuid cs sr =   flip runStateT cs $-  processServerSyncCustom-    ServerSyncProcessor-      { serverSyncProcessorDeleteMany = deleteMany-      , serverSyncProcessorQueryNoLongerSynced = queryNoLongerSynced-      , serverSyncProcessorQueryNewRemote = queryNewRemote-      , serverSyncProcessorInsertMany = insertMany-      }-    sr+    processServerSyncCustom+      ServerSyncProcessor+        { serverSyncProcessorRead = gets serverStoreItems,+          serverSyncProcessorDeleteItems = deleteMany,+          serverSyncProcessorAddItems = insertMany+        }+      sr   where-    deleteMany :: Set i -> StateT (ServerStore i a) m (Set i)+    deleteMany :: Set si -> StateT (ServerStore si a) m (Set si)     deleteMany s = do       modC (`diffSet` s)       pure s-    queryNoLongerSynced :: Set i -> StateT (ServerStore i a) m (Set i)-    queryNoLongerSynced s = query ((s `S.difference`) . M.keysSet)-    queryNewRemote :: Set i -> StateT (ServerStore i a) m (Map i a)-    queryNewRemote s = query (`diffSet` s)-    query :: (Map i a -> b) -> StateT (ServerStore i a) m b-    query func = gets $ func . serverStoreItems-    insertMany :: Map ClientId a -> StateT (ServerStore i a) m (Map ClientId i)+    insertMany :: Map ci a -> StateT (ServerStore si a) m (Map ci si)     insertMany =       traverse $ \a -> do         u <- lift genUuid         ins u a         pure u-    ins :: i -> a -> StateT (ServerStore i a) m ()+    ins :: si -> a -> StateT (ServerStore si a) m ()     ins i val = modC $ M.insert i val-    modC :: (Map i a -> Map i a) -> StateT (ServerStore i a) m ()+    modC :: (Map si a -> Map si a) -> StateT (ServerStore si a) m ()     modC func = modify (\(ServerStore m) -> ServerStore $ func m) -diffSet :: Ord i => Map i a -> Set i -> Map i a+diffSet :: Ord si => Map si a -> Set si -> Map si a diffSet m s = m `M.difference` toMap s -toMap :: Set i -> Map i ()+toMap :: Set si -> Map si () toMap = M.fromSet (const ())  distinct :: Ord a => [a] -> Bool
src/Data/Mergeless/Item.hs view
@@ -117,32 +117,32 @@   case si of     ServerItemEmpty ->       case sr of-        ItemSyncRequestPoll+        ItemSyncRequestPoll ->           -- Both the client and the server think the item is empty, fine.-         -> (ItemSyncResponseInSyncEmpty, si)-        ItemSyncRequestNew a+          (ItemSyncResponseInSyncEmpty, si)+        ItemSyncRequestNew a ->           -- The client has a new item and the server has space for it, add it.-         -> (ItemSyncResponseClientAdded, ServerItemFull a)-        ItemSyncRequestKnown+          (ItemSyncResponseClientAdded, ServerItemFull a)+        ItemSyncRequestKnown ->           -- The client has an item that the server doesn't, so the server must have           -- deleted it when another client asked to do that.           -- Leave it deleted.-         -> (ItemSyncResponseServerDeleted, si)-        ItemSyncRequestDeleted+          (ItemSyncResponseServerDeleted, si)+        ItemSyncRequestDeleted ->           -- The server has deleted an item but the current client hasn't been made aware of that           -- AND this server also deleted that item in the meantime.           -- Just leave it deleted.-         -> (ItemSyncResponseClientDeleted, si)+          (ItemSyncResponseClientDeleted, si)     ServerItemFull s ->       case sr of-        ItemSyncRequestPoll+        ItemSyncRequestPoll ->           -- The server has an item that the client doesn't, send it to the client.-         -> (ItemSyncResponseServerAdded s, si)-        ItemSyncRequestNew _+          (ItemSyncResponseServerAdded s, si)+        ItemSyncRequestNew _ ->           -- The client wants to add an item that the server already has.           -- That means that another client has added that same item in the meantime.           -- This wouldn't happen if the items were named.           -- In this case, for completeness sake,-         -> (ItemSyncResponseServerAdded s, si)+          (ItemSyncResponseServerAdded s, si)         ItemSyncRequestKnown -> (ItemSyncResponseInSyncFull, si)         ItemSyncRequestDeleted -> (ItemSyncResponseClientDeleted, ServerItemEmpty)