module Control.OperationalTransformation.Server ( Revision , ServerState (..) , initialServerState , applyOperation ) where import Control.OperationalTransformation import Control.Monad (foldM) type Revision = Integer -- | The server keeps the current revision number and a list of previous -- operations to transform incoming operations against. data ServerState doc op = ServerState Revision doc [op] initialServerState :: doc -> ServerState doc op initialServerState doc = ServerState 0 doc [] -- | Handles incoming operations. applyOperation :: (OTSystem doc op) => ServerState doc op -> Revision -- ^ The latest operation that the client has received from the server when it sent the operation. -> op -- ^ The operation received from the client. -> Either String (op, ServerState doc op) -- ^ The operation to broadcast to all connected clients -- (except the client which has created the operation; that -- client must be sent an acknowledgement) and the new state -- (or an error). applyOperation (ServerState rev doc ops) oprev op = do concurrentOps <- if oprev > rev || rev - oprev > fromIntegral (length ops) then Left "unknown revision number" else Right $ take (fromInteger $ rev - oprev) ops op' <- foldM transformFst op (reverse concurrentOps) doc' <- case apply op' doc of Left err -> Left $ "apply failed: " ++ err Right d -> Right d return $ (op', ServerState (rev+1) doc' (op':ops)) where transformFst a b = case transform a b of Left err -> Left $ "transform failed: " ++ err Right (a', _) -> Right a'