mergeful-0.2.0.0

Safe HaskellNone
LanguageHaskell2010

Data.Mergeful.Collection

Contents

Description

A way to synchronise a single item with safe merge conflicts.

The setup is as follows:

  • A central server is set up to synchronise with
  • Each client synchronises with the central server, but never with eachother

A client should operate as follows:

The client starts with an initialClientStore.

  • The client produces a SyncRequest with makeSyncRequest.
  • The client sends that request to the central server and gets a SyncResponse.
  • The client then updates its local store with mergeSyncResponseIgnoreProblems.

The central server should operate as follows:

The server starts with an initialServerStore.

WARNING: This whole approach can break down if a server resets its server times or if a client syncs with two different servers using the same server times.

Synopsis

Client side

data ClientStore ci si a Source #

Constructors

ClientStore 

Fields

Instances
(Eq ci, Eq a, Eq si) => Eq (ClientStore ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

(==) :: ClientStore ci si a -> ClientStore ci si a -> Bool #

(/=) :: ClientStore ci si a -> ClientStore ci si a -> Bool #

(Show ci, Show a, Show si) => Show (ClientStore ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

showsPrec :: Int -> ClientStore ci si a -> ShowS #

show :: ClientStore ci si a -> String #

showList :: [ClientStore ci si a] -> ShowS #

Generic (ClientStore ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Associated Types

type Rep (ClientStore ci si a) :: Type -> Type #

Methods

from :: ClientStore ci si a -> Rep (ClientStore ci si a) x #

to :: Rep (ClientStore ci si a) x -> ClientStore ci si a #

(ToJSONKey ci, ToJSONKey si, ToJSON a) => ToJSON (ClientStore ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

toJSON :: ClientStore ci si a -> Value #

toEncoding :: ClientStore ci si a -> Encoding #

toJSONList :: [ClientStore ci si a] -> Value #

toEncodingList :: [ClientStore ci si a] -> Encoding #

(Ord ci, Ord si, FromJSONKey ci, FromJSONKey si, FromJSON a) => FromJSON (ClientStore ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

parseJSON :: Value -> Parser (ClientStore ci si a) #

parseJSONList :: Value -> Parser [ClientStore ci si a] #

(NFData ci, NFData si, NFData a) => NFData (ClientStore ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

rnf :: ClientStore ci si a -> () #

(Validity ci, Validity si, Show ci, Show si, Ord ci, Ord si, Validity a) => Validity (ClientStore ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

validate :: ClientStore ci si a -> Validation #

type Rep (ClientStore ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep (ClientStore ci si a) = D1 (MetaData "ClientStore" "Data.Mergeful.Collection" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "ClientStore" PrefixI True) ((S1 (MetaSel (Just "clientStoreAddedItems") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Map ci a)) :*: S1 (MetaSel (Just "clientStoreSyncedItems") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Map si (Timed a)))) :*: (S1 (MetaSel (Just "clientStoreSyncedButChangedItems") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Map si (Timed a))) :*: S1 (MetaSel (Just "clientStoreDeletedItems") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Map si ServerTime)))))

data Timed a Source #

A value along with a server time.

Constructors

Timed 

Fields

Instances
Eq a => Eq (Timed a) Source # 
Instance details

Defined in Data.Mergeful.Timed

Methods

(==) :: Timed a -> Timed a -> Bool #

(/=) :: Timed a -> Timed a -> Bool #

Show a => Show (Timed a) Source # 
Instance details

Defined in Data.Mergeful.Timed

Methods

showsPrec :: Int -> Timed a -> ShowS #

show :: Timed a -> String #

showList :: [Timed a] -> ShowS #

Generic (Timed a) Source # 
Instance details

Defined in Data.Mergeful.Timed

Associated Types

type Rep (Timed a) :: Type -> Type #

Methods

from :: Timed a -> Rep (Timed a) x #

to :: Rep (Timed a) x -> Timed a #

ToJSON a => ToJSON (Timed a) Source # 
Instance details

Defined in Data.Mergeful.Timed

FromJSON a => FromJSON (Timed a) Source # 
Instance details

Defined in Data.Mergeful.Timed

NFData a => NFData (Timed a) Source # 
Instance details

Defined in Data.Mergeful.Timed

Methods

rnf :: Timed a -> () #

Validity a => Validity (Timed a) Source # 
Instance details

Defined in Data.Mergeful.Timed

Methods

validate :: Timed a -> Validation #

type Rep (Timed a) Source # 
Instance details

Defined in Data.Mergeful.Timed

type Rep (Timed a) = D1 (MetaData "Timed" "Data.Mergeful.Timed" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "Timed" PrefixI True) (S1 (MetaSel (Just "timedValue") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 a) :*: S1 (MetaSel (Just "timedTime") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 ServerTime)))

newtype ServerTime Source #

A "time", as "measured" by the server.

This is closer to a version number than an actual timestamp, but that distinction should not matter for your usage of this library.

In any case, a client should not be changing this value.

We use a Word64 instead of a natural. This will go wrong after 2^64 versions, but since that will not happen in practice, we will not worry about it. You would have to sync millions of modifications every second until long after the sun consumes the earth for this to be a problem.

Constructors

ServerTime 

Fields

Instances
Eq ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

Ord ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

Show ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

Generic ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

Associated Types

type Rep ServerTime :: Type -> Type #

ToJSON ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

FromJSON ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

NFData ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

Methods

rnf :: ServerTime -> () #

Validity ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

type Rep ServerTime Source # 
Instance details

Defined in Data.Mergeful.Timed

type Rep ServerTime = D1 (MetaData "ServerTime" "Data.Mergeful.Timed" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" True) (C1 (MetaCons "ServerTime" PrefixI True) (S1 (MetaSel (Just "unServerTime") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Word64)))

initialClientStore :: ClientStore ci si a Source #

A client store to start with.

This store contains no items.

Querying the client store

clientStoreSize :: ClientStore ci si a -> Word Source #

The number of items in a client store

This does not count the deleted items, so that they really look deleted..

clientStoreClientIdSet :: ClientStore ci si a -> Set ci Source #

The set of client ids.

These are only the client ids of the added items that have not been synced yet.

clientStoreUndeletedSyncIdSet :: Ord si => ClientStore ci si a -> Set si Source #

The set of server ids.

This does not include the ids of items that have been marked as deleted.

clientStoreSyncIdSet :: Ord si => ClientStore ci si a -> Set si Source #

The set of server ids.

This includes the ids of items that have been marked as deleted.

clientStoreItems :: (Ord ci, Ord si) => ClientStore ci si a -> Map (Either ci si) a Source #

The set of items in the client store

This map does not include items that have been marked as deleted.

Changing the client store

addItemToClientStore :: (Ord ci, Enum ci, Bounded ci) => a -> ClientStore ci si a -> ClientStore ci si a Source #

Add an item to a client store as an added item.

This will take care of the uniqueness constraint of the cis in the map.

findFreeSpot :: (Ord ci, Enum ci, Bounded ci) => Map ci a -> ci Source #

Find a free client id to use

You shouldn't need this function, addItemToClientStore takes care of this.

The values wrap around when reaching maxBound.

markItemDeletedInClientStore :: Ord si => si -> ClientStore ci si a -> ClientStore ci si a Source #

Mark an item deleted in a client store.

This function will not delete the item, but mark it as deleted instead.

changeItemInClientStore :: Ord si => si -> a -> ClientStore ci si a -> ClientStore ci si a Source #

Replace the given item with a new value.

This function will correctly mark the item as changed, if it exist.

It will not add an item to the store with the given id, because the server may not have been the origin of that id.

deleteItemFromClientStore :: Ord ci => ci -> ClientStore ci si a -> ClientStore ci si a Source #

Delete an unsynced item from a client store.

This function will immediately delete the item, because it has never been synced.

Maxing a sync request

data SyncRequest ci si a Source #

Constructors

SyncRequest 

Fields

Instances
(Eq ci, Eq a, Eq si) => Eq (SyncRequest ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

(==) :: SyncRequest ci si a -> SyncRequest ci si a -> Bool #

(/=) :: SyncRequest ci si a -> SyncRequest ci si a -> Bool #

(Show ci, Show a, Show si) => Show (SyncRequest ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

showsPrec :: Int -> SyncRequest ci si a -> ShowS #

show :: SyncRequest ci si a -> String #

showList :: [SyncRequest ci si a] -> ShowS #

Generic (SyncRequest ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Associated Types

type Rep (SyncRequest ci si a) :: Type -> Type #

Methods

from :: SyncRequest ci si a -> Rep (SyncRequest ci si a) x #

to :: Rep (SyncRequest ci si a) x -> SyncRequest ci si a #

(ToJSONKey ci, ToJSONKey si, ToJSON a) => ToJSON (SyncRequest ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

toJSON :: SyncRequest ci si a -> Value #

toEncoding :: SyncRequest ci si a -> Encoding #

toJSONList :: [SyncRequest ci si a] -> Value #

toEncodingList :: [SyncRequest ci si a] -> Encoding #

(Ord ci, Ord si, FromJSONKey ci, FromJSONKey si, FromJSON a) => FromJSON (SyncRequest ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

parseJSON :: Value -> Parser (SyncRequest ci si a) #

parseJSONList :: Value -> Parser [SyncRequest ci si a] #

(NFData ci, NFData si, NFData a) => NFData (SyncRequest ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

rnf :: SyncRequest ci si a -> () #

(Validity ci, Validity si, Show ci, Show si, Ord ci, Ord si, Validity a) => Validity (SyncRequest ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

validate :: SyncRequest ci si a -> Validation #

type Rep (SyncRequest ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep (SyncRequest ci si a) = D1 (MetaData "SyncRequest" "Data.Mergeful.Collection" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "SyncRequest" PrefixI True) ((S1 (MetaSel (Just "syncRequestNewItems") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map ci a)) :*: S1 (MetaSel (Just "syncRequestKnownItems") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si ServerTime))) :*: (S1 (MetaSel (Just "syncRequestKnownButChangedItems") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si (Timed a))) :*: S1 (MetaSel (Just "syncRequestDeletedItems") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si ServerTime)))))

initialSyncRequest :: SyncRequest ci si a Source #

An intial SyncRequest to start with.

It just asks the server to send over whatever it knows.

makeSyncRequest :: ClientStore ci si a -> SyncRequest ci si a Source #

Produce an SyncRequest from a ClientStore.

Send this to the server for synchronisation.

Merging the response

data SyncResponse ci si a Source #

Constructors

SyncResponse 

Fields

  • syncResponseClientAdded :: !(Map ci (ClientAddition si))

    The client added these items and server has succesfully been made aware of that.

    The client needs to update their server times

  • syncResponseClientChanged :: !(Map si ServerTime)

    The client changed these items and server has succesfully been made aware of that.

    The client needs to update their server times

  • syncResponseClientDeleted :: !(Set si)

    The client deleted these items and server has succesfully been made aware of that.

    The client can delete them from its deleted items

  • syncResponseServerAdded :: !(Map si (Timed a))

    These items have been added on the server side

    The client should add them too.

  • syncResponseServerChanged :: !(Map si (Timed a))

    These items have been modified on the server side.

    The client should modify them too.

  • syncResponseServerDeleted :: !(Set si)

    These items were deleted on the server side

    The client should delete them too

  • syncResponseConflicts :: !(Map si (Timed a))

    These are conflicts where the server and the client both have an item, but it is different.

    The server kept its part of each, the client can either take whatever the server gave them or deal with the conflicts somehow, and then try to re-sync.

  • syncResponseConflictsClientDeleted :: !(Map si (Timed a))

    These are conflicts where the server has an item but the client does not.

    The server kept its item, the client can either take whatever the server gave them or deal with the conflicts somehow, and then try to re-sync.

  • syncResponseConflictsServerDeleted :: !(Set si)

    These are conflicts where the server has no item but the client has a modified item.

    The server left its item deleted, the client can either delete its items too or deal with the conflicts somehow, and then try to re-sync.

Instances
(Eq ci, Eq si, Eq a) => Eq (SyncResponse ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

(==) :: SyncResponse ci si a -> SyncResponse ci si a -> Bool #

(/=) :: SyncResponse ci si a -> SyncResponse ci si a -> Bool #

(Show ci, Show si, Show a) => Show (SyncResponse ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

showsPrec :: Int -> SyncResponse ci si a -> ShowS #

show :: SyncResponse ci si a -> String #

showList :: [SyncResponse ci si a] -> ShowS #

Generic (SyncResponse ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Associated Types

type Rep (SyncResponse ci si a) :: Type -> Type #

Methods

from :: SyncResponse ci si a -> Rep (SyncResponse ci si a) x #

to :: Rep (SyncResponse ci si a) x -> SyncResponse ci si a #

(ToJSON ci, ToJSON si, ToJSONKey ci, ToJSONKey si, ToJSON a) => ToJSON (SyncResponse ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

toJSON :: SyncResponse ci si a -> Value #

toEncoding :: SyncResponse ci si a -> Encoding #

toJSONList :: [SyncResponse ci si a] -> Value #

toEncodingList :: [SyncResponse ci si a] -> Encoding #

(Ord ci, Ord si, FromJSON ci, FromJSON si, FromJSONKey ci, FromJSONKey si, FromJSON a) => FromJSON (SyncResponse ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

parseJSON :: Value -> Parser (SyncResponse ci si a) #

parseJSONList :: Value -> Parser [SyncResponse ci si a] #

(NFData ci, NFData si, NFData a) => NFData (SyncResponse ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

rnf :: SyncResponse ci si a -> () #

(Validity ci, Validity si, Show ci, Show si, Ord ci, Ord si, Validity a) => Validity (SyncResponse ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

validate :: SyncResponse ci si a -> Validation #

type Rep (SyncResponse ci si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep (SyncResponse ci si a) = D1 (MetaData "SyncResponse" "Data.Mergeful.Collection" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "SyncResponse" PrefixI True) (((S1 (MetaSel (Just "syncResponseClientAdded") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map ci (ClientAddition si))) :*: S1 (MetaSel (Just "syncResponseClientChanged") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si ServerTime))) :*: (S1 (MetaSel (Just "syncResponseClientDeleted") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Set si)) :*: S1 (MetaSel (Just "syncResponseServerAdded") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si (Timed a))))) :*: ((S1 (MetaSel (Just "syncResponseServerChanged") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si (Timed a))) :*: S1 (MetaSel (Just "syncResponseServerDeleted") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Set si))) :*: (S1 (MetaSel (Just "syncResponseConflicts") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si (Timed a))) :*: (S1 (MetaSel (Just "syncResponseConflictsClientDeleted") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si (Timed a))) :*: S1 (MetaSel (Just "syncResponseConflictsServerDeleted") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Set si)))))))

data ClientAddition i Source #

Instances
Eq i => Eq (ClientAddition i) Source # 
Instance details

Defined in Data.Mergeful.Collection

Show i => Show (ClientAddition i) Source # 
Instance details

Defined in Data.Mergeful.Collection

Generic (ClientAddition i) Source # 
Instance details

Defined in Data.Mergeful.Collection

Associated Types

type Rep (ClientAddition i) :: Type -> Type #

ToJSON i => ToJSON (ClientAddition i) Source # 
Instance details

Defined in Data.Mergeful.Collection

FromJSON i => FromJSON (ClientAddition i) Source # 
Instance details

Defined in Data.Mergeful.Collection

NFData i => NFData (ClientAddition i) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

rnf :: ClientAddition i -> () #

Validity i => Validity (ClientAddition i) Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep (ClientAddition i) Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep (ClientAddition i) = D1 (MetaData "ClientAddition" "Data.Mergeful.Collection" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "ClientAddition" PrefixI True) (S1 (MetaSel (Just "clientAdditionId") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 i) :*: S1 (MetaSel (Just "clientAdditionServerTime") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 ServerTime)))

data ItemMergeStrategy a Source #

A strategy to merge conflicts for item synchronisation

Constructors

ItemMergeStrategy 

Fields

Instances
Generic (ItemMergeStrategy a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ItemMergeStrategy a) :: Type -> Type #

type Rep (ItemMergeStrategy a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ItemMergeStrategy a) = D1 (MetaData "ItemMergeStrategy" "Data.Mergeful.Item" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "ItemMergeStrategy" PrefixI True) (S1 (MetaSel (Just "itemMergeStrategyMergeChangeConflict") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (a -> a -> ChangeConflictResolution a)) :*: (S1 (MetaSel (Just "itemMergeStrategyMergeClientDeletedConflict") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (a -> ClientDeletedConflictResolution)) :*: S1 (MetaSel (Just "itemMergeStrategyMergeServerDeletedConflict") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (a -> ServerDeletedConflictResolution)))))

data ChangeConflictResolution a Source #

Constructors

KeepLocal 
TakeRemote 
Merged a 
Instances
Eq a => Eq (ChangeConflictResolution a) Source # 
Instance details

Defined in Data.Mergeful.Item

Show a => Show (ChangeConflictResolution a) Source # 
Instance details

Defined in Data.Mergeful.Item

Generic (ChangeConflictResolution a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ChangeConflictResolution a) :: Type -> Type #

type Rep (ChangeConflictResolution a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ChangeConflictResolution a) = D1 (MetaData "ChangeConflictResolution" "Data.Mergeful.Item" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "KeepLocal" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "TakeRemote" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "Merged" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 a))))

mergeFromServerStrategy :: ItemMergeStrategy a Source #

A merge strategy that takes whatever the server gave the client.

Pro: Clients will converge on the same value.

Con: Conflicting updates will be lost.

mergeFromClientStrategy :: ItemMergeStrategy a Source #

A merge strategy that keeps whatever the client had.

Pro: does not lose data

Con: Clients will diverge when a conflict occurs

mergeUsingCRDTStrategy :: (a -> a -> a) -> ItemMergeStrategy a Source #

A merge strategy that uses a CRDT merging function to merge items.

In case of other-than-change conflicts, this will be the same as the mergeFromServerStrategy strategy. If this is not what you want, create your own ItemMergeStrategy manually.

mergeSyncResponseFromServer :: (Ord ci, Ord si) => ClientStore ci si a -> SyncResponse ci si a -> ClientStore ci si a Source #

Merge a SyncResponse into the current ClientStore by taking whatever the server gave the client in case of conflict.

Pro: Clients will converge on the same value.

Con: Conflicting updates will be lost.

mergeSyncResponseFromClient :: (Ord ci, Ord si) => ClientStore ci si a -> SyncResponse ci si a -> ClientStore ci si a Source #

Merge a SyncResponse into the current ClientStore by keeping whatever the client had in case of conflict.

Pro: No data will be lost

Con: Clients will diverge when conflicts occur.

mergeSyncResponseUsingCRDT :: (Ord ci, Ord si) => (a -> a -> a) -> ClientStore ci si a -> SyncResponse ci si a -> ClientStore ci si a Source #

Merge a SyncResponse into the current ClientStore by using the given GADT merging function in case of conflict

mergeSyncResponseUsingStrategy :: (Ord ci, Ord si) => ItemMergeStrategy a -> ClientStore ci si a -> SyncResponse ci si a -> ClientStore ci si a Source #

Merge an SyncResponse into the current ClientStore with the given merge strategy.

In order for clients to converge on the same collection correctly, this function must be:

  • Associative
  • Idempotent
  • The same on all clients

This function ignores mismatches.

data ClientSyncProcessor ci si a (m :: * -> *) Source #

Constructors

ClientSyncProcessor 

Fields

Instances
Generic (ClientSyncProcessor ci si a m) Source # 
Instance details

Defined in Data.Mergeful.Collection

Associated Types

type Rep (ClientSyncProcessor ci si a m) :: Type -> Type #

Methods

from :: ClientSyncProcessor ci si a m -> Rep (ClientSyncProcessor ci si a m) x #

to :: Rep (ClientSyncProcessor ci si a m) x -> ClientSyncProcessor ci si a m #

type Rep (ClientSyncProcessor ci si a m) Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep (ClientSyncProcessor ci si a m) = D1 (MetaData "ClientSyncProcessor" "Data.Mergeful.Collection" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "ClientSyncProcessor" PrefixI True) (((S1 (MetaSel (Just "clientSyncProcessorQuerySyncedButChangedValues") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Set si -> m (Map si (Timed a)))) :*: S1 (MetaSel (Just "clientSyncProcessorSyncClientAdded") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map ci (ClientAddition si) -> m ()))) :*: (S1 (MetaSel (Just "clientSyncProcessorSyncClientChanged") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si ServerTime -> m ())) :*: S1 (MetaSel (Just "clientSyncProcessorSyncClientDeleted") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Set si -> m ())))) :*: ((S1 (MetaSel (Just "clientSyncProcessorSyncMergedConflict") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si (Timed a) -> m ())) :*: S1 (MetaSel (Just "clientSyncProcessorSyncServerAdded") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si (Timed a) -> m ()))) :*: (S1 (MetaSel (Just "clientSyncProcessorSyncServerChanged") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Map si (Timed a) -> m ())) :*: S1 (MetaSel (Just "clientSyncProcessorSyncServerDeleted") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (Set si -> m ()))))))

Utility functions for implementing pure client-side merging

newtype ClientId Source #

A Client-side identifier for items.

These only need to be unique at the client.

Constructors

ClientId 

Fields

Instances
Bounded ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

Enum ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

Eq ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

Ord ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

Show ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

Generic ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

Associated Types

type Rep ClientId :: Type -> Type #

Methods

from :: ClientId -> Rep ClientId x #

to :: Rep ClientId x -> ClientId #

ToJSON ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

ToJSONKey ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

FromJSON ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

FromJSONKey ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

NFData ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

rnf :: ClientId -> () #

Validity ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep ClientId Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep ClientId = D1 (MetaData "ClientId" "Data.Mergeful.Collection" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" True) (C1 (MetaCons "ClientId" PrefixI True) (S1 (MetaSel (Just "unClientId") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Word64)))

mergeAddedItems :: forall ci si a. (Ord ci, Ord si) => Map ci a -> Map ci (ClientAddition si) -> (Map ci a, Map si (Timed a)) Source #

Merge the local added items with the ones that the server has acknowledged as added.

mergeSyncedButChangedItems :: forall i a. Ord i => Map i (Timed a) -> Map i ServerTime -> (Map i (Timed a), Map i (Timed a)) Source #

Merge the local synced but changed items with the ones that the server has acknowledged as changed.

mergeDeletedItems :: Ord i => Map i b -> Set i -> Map i b Source #

Merge the local deleted items with the ones that the server has acknowledged as deleted.

Utility functions for implementing custom client-side merging

mergeSyncedButChangedConflicts Source #

Arguments

:: Ord si 
=> (a -> a -> ChangeConflictResolution a) 
-> Map si (Timed a)

The conflicting items on the client side

-> Map si (Timed a)

The conflicting items on the server side

-> (Map si (Timed a), Map si (Timed a), Map si (Timed a))

Unresolved conflicts on the left, merged conflicts in the middle, resolved conflicts on the right

  • The unresolved conflicts should remain as-is
  • The merged conflicts should be updated and marked as changed
  • The resolved conflicts should be updated and marked as unchanged

Resolve change conflicts

mergeClientDeletedConflicts Source #

Arguments

:: (a -> ClientDeletedConflictResolution) 
-> Map si (Timed a)

The conflicting items on the server side

-> Map si (Timed a)

A map of items that need to be updated on the client.

Resolve client deleted conflicts

mergeServerDeletedConflicts Source #

Arguments

:: (a -> ServerDeletedConflictResolution) 
-> Map si (Timed a)

The conflicting items on the client side

-> Set si

The result is a map of items that need to be deleted on the client.

Resolve server deleted conflicts

Server side

The store

newtype ServerStore si a Source #

Constructors

ServerStore 

Fields

Instances
(Eq si, Eq a) => Eq (ServerStore si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

(==) :: ServerStore si a -> ServerStore si a -> Bool #

(/=) :: ServerStore si a -> ServerStore si a -> Bool #

(Show si, Show a) => Show (ServerStore si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

showsPrec :: Int -> ServerStore si a -> ShowS #

show :: ServerStore si a -> String #

showList :: [ServerStore si a] -> ShowS #

Generic (ServerStore si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Associated Types

type Rep (ServerStore si a) :: Type -> Type #

Methods

from :: ServerStore si a -> Rep (ServerStore si a) x #

to :: Rep (ServerStore si a) x -> ServerStore si a #

(ToJSON a, ToJSONKey si) => ToJSON (ServerStore si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

(FromJSONKey si, Ord si, FromJSON a) => FromJSON (ServerStore si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

(NFData si, NFData a) => NFData (ServerStore si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

rnf :: ServerStore si a -> () #

(Validity si, Show si, Ord si, Validity a) => Validity (ServerStore si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

Methods

validate :: ServerStore si a -> Validation #

type Rep (ServerStore si a) Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep (ServerStore si a) = D1 (MetaData "ServerStore" "Data.Mergeful.Collection" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" True) (C1 (MetaCons "ServerStore" PrefixI True) (S1 (MetaSel (Just "serverStoreItems") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Map si (Timed a)))))

initialServerStore :: ServerStore si a Source #

A server store to start with

This store contains no items.

Processing a sync request

processServerSync Source #

Arguments

:: (Ord si, Monad m) 
=> m si

The action that is guaranteed to generate unique identifiers

-> ServerStore si a 
-> SyncRequest ci si a 
-> m (SyncResponse ci si a, ServerStore si a) 

Serve an SyncRequest using the current ServerStore, producing an SyncResponse and a new ServerStore.

data ServerSyncProcessor ci si a m Source #

Constructors

ServerSyncProcessor 

Fields

Instances
Generic (ServerSyncProcessor ci si a m) Source # 
Instance details

Defined in Data.Mergeful.Collection

Associated Types

type Rep (ServerSyncProcessor ci si a m) :: Type -> Type #

Methods

from :: ServerSyncProcessor ci si a m -> Rep (ServerSyncProcessor ci si a m) x #

to :: Rep (ServerSyncProcessor ci si a m) x -> ServerSyncProcessor ci si a m #

type Rep (ServerSyncProcessor ci si a m) Source # 
Instance details

Defined in Data.Mergeful.Collection

type Rep (ServerSyncProcessor ci si a m) = D1 (MetaData "ServerSyncProcessor" "Data.Mergeful.Collection" "mergeful-0.2.0.0-Czn7HU9ZrLaB0R8xfAleR6" False) (C1 (MetaCons "ServerSyncProcessor" PrefixI True) ((S1 (MetaSel (Just "serverSyncProcessorRead") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (m (Map si (Timed a)))) :*: S1 (MetaSel (Just "serverSyncProcessorAddItem") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (a -> m si))) :*: (S1 (MetaSel (Just "serverSyncProcessorChangeItem") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (si -> ServerTime -> a -> m ())) :*: S1 (MetaSel (Just "serverSyncProcessorDeleteItem") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 (si -> m ())))))

processServerSyncCustom Source #

Arguments

:: (Ord si, Monad m) 
=> ServerSyncProcessor ci si a m

Your server sync processor

-> SyncRequest ci si a 
-> m (SyncResponse ci si a) 

Process a server sync

Implementation Details

Expand

There are four cases for the items in the sync request

  • Added (A)
  • Synced (S)
  • Changed (C)
  • Deleted (D)

Each of them present options and may require action on the sever side:

  • Added:

    • Client Added (CA) (This is the only case where a new identifier needs to be generated.)
  • Synced:

    • Server Changed (SC) (Nothing)
    • Server Deleted (SD) (Nothing)
  • Changed:

    • Client Changed (CC) (Update value and increment server time)
    • Change Conflict (CConf) (Nothing)
    • Server Deleted Conflict (SDC) (Nothing)
  • Deleted:

    • Client Deleted (CD) (Delete the item)
    • Client Deleted Conflict (CDC) (Nothing)
  • Extra:

    • Server Added (SA) (Nothing)

For more detailed comments of the nine cases, see the source of processServerItemSync in the Data.Mergeful.Item.

emptySyncResponse :: SyncResponse ci si a Source #

A sync response to start with.

It is entirely empty.

initialServerTime :: ServerTime Source #

A server time to start with.

incrementServerTime :: ServerTime -> ServerTime Source #

Increment a server time.