{-# OPTIONS -fglasgow-exts #-} module Data.SafeCopy.Types ( SafeCopy(..) , Migrate(..) , Mode(..) , Contained , contain , Proxy(..) , mkProxy , Previous , mkPrevious -- , version , getSafeGet , getSafePut , safePut , safeGet , safeGetVersioned ) where import Data.Binary as B import Data.Binary.Put as B import Data.Binary.Get as B import qualified Data.ByteString.Lazy.Char8 as L import Control.Monad data Contained a = Contained {unsafeUnPack :: a} contain :: a -> Contained a contain = Contained data Proxy a = Proxy mkProxy :: a -> Proxy a mkProxy _ = Proxy asProxyType :: a -> Proxy a -> a asProxyType a _ = a class Migrate a b where migrate :: a -> b data Previous a = forall b. (SafeCopy b, Migrate b a) => Previous (Proxy b) mkPrevious :: forall a b. (SafeCopy b, Migrate b a) => Proxy b -> Previous a mkPrevious Proxy = Previous (Proxy :: Proxy b) newtype Version a = Version {unVersion :: Int} deriving (Num,Read,Show,Eq) data Mode a = Primitive | Base | Extension (Previous a) class SafeCopy a where version :: Version a version = 0 mode :: Mode a mode = Base getCopy :: Contained (Get a) putCopy :: a -> Contained Put instance Binary (Version a) where get = liftM Version get put = put . unVersion getSafeGet :: forall a. SafeCopy a => Get (Get a) getSafeGet = case mode :: Mode a of Primitive -> return (unsafeUnPack getCopy) _ -> do v <- get return (safeGetVersioned v) getSafePut :: forall a. SafeCopy a => PutM (a -> Put) getSafePut = case mode :: Mode a of Primitive -> return (unsafeUnPack . putCopy) _ -> do B.put (version :: Version a) return (unsafeUnPack . putCopy) safePut :: forall a. SafeCopy a => a -> Put safePut val = do fn <- getSafePut fn val safeGet :: forall a. SafeCopy a => Get a safeGet = join getSafeGet safeGetVersioned :: forall a b. SafeCopy b => Version a -> B.Get b safeGetVersioned v = case compareVersions v (version :: Version b) of GT -> error $ "Version tag too large: " ++ show v EQ -> unsafeUnPack getCopy LT -> case mode of Extension (Previous (_ :: Proxy f) :: Previous b) -> do old <- safeGetVersioned v :: B.Get f return $ migrate old _ -> error $ "No previous version" compareVersions :: Version a -> Version b -> Ordering compareVersions v1 v2 = compare (unVersion v1) (unVersion v2) {- class Versioned a where typeVersion :: a -> Int data Test1 a = Test1 a data Test2 a = Test2 a data Sub1 = Sub1 Int; instance Versioned Sub1 where typeVersion _ = 1 data Sub2 = Sub2 String; instance Versioned Sub2 where typeVersion _ = 2 showVersion (Test1 s) = typeVersion s -} {- class HotSwap a where hotVersion :: Version a hotVersion = 0 destruct :: a -> (Int,ArgList) construct :: (Int,ArgList) -> a data Test = Test Sub1 Sub2 data Sub1 = Sub1 data Sub2 = Sub2 data ArgList = Nil | forall a. HotSwap a => a :+: ArgList infixr 5 :+: instance HotSwap Test where destruct (Test a b) = (1, a :+: b :+: Nil) construct (1, a :+: b :+: Nil) = Test (fromHot a) (fromHot b) instance HotSwap Sub1 where destruct Sub1 = (1,Nil) construct (1,Nil) = Sub1 instance HotSwap Sub2 where destruct Sub2= (1,Nil) construct (1,Nil) = Sub2 fromHot :: (HotSwap a, HotSwap b) => a -> b fromHot = undefined {- hotSwap :: forall a b. (HotSwap a, HotSwap b) => a -> b hotSwap inp = let inpVersion = hotVersion :: Version a outVersion = hotVersion :: Version b in -} -}