concrete-haskell-0.1.0.16: src/Data/Concrete/Services/Store.hs
{-# LANGUAGE DeriveGeneric, OverloadedStrings, FlexibleInstances, PackageImports #-}
{-|
Description: Implementations of StoreCommunicationService
-}
module Data.Concrete.Services.Store ( ZipStore(..)
, TarStore(..)
, HandleStore(..)
, process
, makeTarStore
, makeZipStore
, makeHandleStore
, storeDirect
) where
import qualified Data.ByteString as SBS
import qualified Data.ByteString.Lazy as LBS
import qualified Codec.Compression.GZip as GZip
import qualified Codec.Compression.BZip as BZip
import qualified "zip" Codec.Archive.Zip as Zip
import qualified "zip-conduit" Codec.Archive.Zip as ZipC
import qualified Codec.Archive.Tar as Tar
import qualified Codec.Archive.Tar.Entry as Tar
import qualified Codec.Archive.Tar.Index as Tar
import System.IO (Handle, hSeek, hTell, hFlush, SeekMode(..))
import qualified Data.Text.Lazy as T
import qualified Data.List as L
import Path.IO (resolveFile', doesFileExist)
import Data.Either (rights)
import Path (Path, Dir, File, Abs, filename)
import Data.Concrete.Services (Compression(..))
import Data.Concrete.Autogen.Communication_Types (Communication(..), default_Communication, read_Communication, write_Communication)
import Data.Concrete.Autogen.Service_Iface (Service_Iface(about, alive))
import Data.Concrete.Autogen.Services_Types (ServiceInfo(..))
import Data.Concrete.Autogen.StoreCommunicationService_Iface (StoreCommunicationService_Iface(store))
import Data.Concrete.Autogen.StoreCommunicationService (process)
import Data.Concrete.Utils (commToString)
import System.IO (openFile, IOMode(..))
import Control.Monad (liftM)
import System.FilePath (takeExtension)
import Control.Monad.IO.Class (liftIO)
import Conduit
import Data.Void (Void)
storeDirect :: StoreCommunicationService_Iface a => a -> [Communication] -> IO ()
storeDirect s cs = (sequence $ map (store s) cs) >> return ()
lift1st :: Monad m => (m a, b) -> m (a, b)
lift1st (f, s) = do
f' <- f
return (f', s)
-- | Handle-based Store backend
newtype HandleStore = HandleStore (Handle, (LBS.ByteString -> LBS.ByteString))
instance Service_Iface HandleStore where
about _ = return $ ServiceInfo "Flat-file-backed StoreCommunicationService" "0.0.1" (Just "Haskell implementation")
alive _ = return True
instance StoreCommunicationService_Iface HandleStore where
store (HandleStore (h, c)) comm = do
t <- commToString comm
LBS.hPutStr h (c t)
makeHandleStore :: String -> IO HandleStore
makeHandleStore f = do
let c = case takeExtension f of
".gz" -> GZip.compress
".bz2" -> BZip.compress
_ -> id
fd <- openFile f WriteMode
return $ HandleStore (fd, c)
-- | Zip-based Store backend
newtype ZipStore = ZipStore (Path Abs File)
instance Service_Iface ZipStore where
about _ = return $ ServiceInfo "Zip-backed StoreCommunicationService" "0.0.1" (Just "Haskell implementation")
alive _ = return True
instance StoreCommunicationService_Iface ZipStore where
store (ZipStore f) c = do
bs <- commToString c
p <- filename <$> resolveFile' ((T.unpack . communication_id) c)
es <- Zip.mkEntrySelector p
Zip.withArchive f $ Zip.addEntry Zip.Deflate (LBS.toStrict bs) es
-- | Create a Zip-backed Store handler based on the given file
makeZipStore :: String -> IO ZipStore
makeZipStore f = do
f' <- resolveFile' f
e <- doesFileExist f'
if e == False then Zip.createArchive f' $ return () else return ()
return $ ZipStore f'
-- | Tar-based Store backend
newtype TarStore = TarStore (Handle, (LBS.ByteString -> LBS.ByteString), SBS.ByteString, Integer)
-- | Create a Tar-backed Store handler based on the given file
makeTarStore :: String -> IO TarStore
makeTarStore f = do
(h, c) <- case takeExtension f of
--".tgz" -> lift1st (openFile f WriteMode, GZip.compress)
--".tbz2" -> lift1st (openFile f WriteMode, BZip.compress)
".tar" -> lift1st (openFile f WriteMode, id)
let pad = LBS.toStrict (c (LBS.replicate (1024) 0))
return $ TarStore (h, c, pad, fromIntegral $ - (SBS.length pad))
instance Service_Iface TarStore where
about _ = return $ ServiceInfo "Tar-backed StoreCommunicationService" "0.0.1" (Just "Haskell implementation")
alive _ = return True
instance StoreCommunicationService_Iface TarStore where
store (TarStore (h, c, pad, o)) comm = do
cur <- hTell h
if cur == 0 then return () else hSeek h RelativeSeek o
t <- commToString comm
let e = Tar.fileEntry ((L.head . rights) [Tar.toTarPath False ((T.unpack . communication_id) comm)]) t
bs' = Tar.write [e]
bs'' = LBS.toStrict bs'
l = SBS.length bs''
bs''' = SBS.take (l - 1024) bs''
LBS.hPutStr h (c (LBS.fromStrict bs'''))
SBS.hPutStr h pad
hFlush h