concrete-haskell-0.1.0.13: src/Data/Concrete/Services/Fetch.hs
{-# LANGUAGE DeriveGeneric, OverloadedStrings, FlexibleInstances, BangPatterns #-}
module Data.Concrete.Services.Fetch ( HandleFetch(..)
, ZipFetch(..)
, TarFetch(..)
, makeTarFetch
, process
) where
import System.IO (Handle)
import qualified Data.Vector as V
import qualified Codec.Archive.Tar as Tar
import qualified Codec.Archive.Tar.Entry as Tar
import qualified Codec.Archive.Tar.Index as Tar
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 Codec.Archive.Zip as Zip
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 (openFile, IOMode(..), hTell)
import System.FilePath (takeExtension)
import Data.Either (rights)
import qualified Data.Map.Strict as Map
import Data.Map.Strict (Map)
import Data.Concrete.Utils (stringToComm)
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.Access_Types (FetchResult(..), default_FetchResult, FetchRequest(..))
import Data.Concrete.Autogen.FetchCommunicationService_Iface (FetchCommunicationService_Iface(fetch, getCommunicationIDs, getCommunicationCount))
import Data.Concrete.Autogen.FetchCommunicationService (process)
import Data.List (genericDrop, genericTake, genericLength)
import Data.Text.Lazy (Text, pack, unpack)
-- Handle-backed
newtype HandleFetch = HandleFetch (Handle, Maybe Compression)
-- Zip-backed
newtype ZipFetch = ZipFetch (Handle, Maybe Compression)
-- Tar-backed
newtype TarFetch = TarFetch (Handle, (LBS.ByteString -> LBS.ByteString), Tar.TarIndex, Map String FilePath)
instance Service_Iface TarFetch where
about _ = return $ ServiceInfo "Tar-backed FetchCommunicationService" "0.0.1" (Just "Haskell implementation")
alive _ = return True
instance FetchCommunicationService_Iface TarFetch where
fetch (TarFetch (h, c, i, l)) ii = do
cc <- sequence $ map fetchOne ((map unpack . V.toList . fetchRequest_communicationIds) ii)
return $ default_FetchResult { fetchResult_communications=V.fromList cc }
where
fetchOne :: String -> IO Communication
fetchOne p = do
let (Just (Tar.TarFileEntry o)) = Tar.lookup i (l Map.! p)
e <- Tar.hReadEntry h o
stringToComm ((((\ (Tar.NormalFile bs _) -> bs) . Tar.entryContent) ) e)
getCommunicationIDs (TarFetch (_, _, _, l)) offset count = return $ V.fromList $ ((map (pack . fst)) . genericTake count . genericDrop offset . Map.toList) l
getCommunicationCount (TarFetch (_, _, _, l)) = return ((genericLength . Map.toList) l)
makeTarFetch :: String -> IO TarFetch
makeTarFetch f = do
let c = case takeExtension f of
--".tgz" -> GZip.decompress
--".tbz2" -> BZip.decompress
".tar" -> id
h <- openFile f ReadMode
bs <- c <$> LBS.readFile f
let e = Tar.read bs
(l, i) <- build e
h' <- openFile f ReadMode
return $ TarFetch (h, c, i, Map.fromList l)
commFromEntry :: Tar.Entry -> IO Communication
commFromEntry e = stringToComm (((\ (Tar.NormalFile bs _) -> bs) . Tar.entryContent) e)
build :: Tar.Entries e -> IO ([(String, FilePath)], Tar.TarIndex)
build = go ([], Tar.empty)
where
go :: ([(String, FilePath)], Tar.IndexBuilder) -> Tar.Entries e -> IO ([(String, FilePath)], Tar.TarIndex)
go (l, !builder) (Tar.Next e es) = do
c <- commFromEntry e
go (((unpack . communication_id) c, Tar.entryPath e):l, Tar.addNextEntry e builder) es
go (l, !builder) (Tar.Done) = do
return (l, Tar.finalise builder)