packages feed

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)