packages feed

mptcpanalyzer-0.0.2.0: src/MptcpAnalyzer/Cache.hs

{-|
Module      : MptcpAnalyzer.Cache
Description : Save into/load from a local cache
Maintainer  : matt
License     : GPL-3
-}

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
module MptcpAnalyzer.Cache (
  CacheId(..)
  , Cache(..)
  , CacheConfig(..)
  , runMockCache
  , getCache
  , putCache
  , runCache
)
where

import MptcpAnalyzer.Pcap
import MptcpAnalyzer.Types

import Prelude hiding (writeFile)
import System.Directory (doesFileExist)
-- import System.Posix.Files.ByteString
import Control.Exception as CE
import Data.ByteString (writeFile)
import Data.Hashable
import Data.List (intercalate)
import Data.Serialize
import Frames
import Frames.CSV
import GHC.Generics
import Polysemy
import System.FilePath.Posix (takeBaseName)


-- | Cache item identifier
data CacheId = CacheId {
  cacheDeps :: [FilePath]
  , cachePrefix :: String
  , cacheSuffix :: String
} deriving (Generic, Show, Eq, Hashable)

-- | Cache config (this is a test)
-- test
data CacheConfig = CacheConfig {
  -- | Folder where to store stuff
  cacheFolder :: FilePath
  -- | Whether the cachie is fixed
  , cacheEnabled :: Bool
} deriving Show

-- class Serializable a where
--   encode :: a -> String
--   decode :: String -> a

-- TODO add a cacheConfig ?
-- TODO this should be an effect
data Cache m a where
    -- should maybe be a filepath
    PutCache :: Serialize res => CacheId -> res -> Cache m Bool
    GetCache :: Serialize res => CacheId -> Cache m (Either String res)
    IsValid :: CacheId -> Cache m Bool

makeSem ''Cache

filenameFromCacheId :: CacheId -> FilePath
filenameFromCacheId cid =
    cachePrefix cid ++ intercalate "_" basenames ++ show myHash ++ cacheSuffix cid
    where
        basenames = map takeBaseName $ cacheDeps cid
        -- TODO
        -- "hash"
        myHash = hash basenames

-- Return full path to the config folder
getFullPath :: CacheConfig -> CacheId -> FilePath
getFullPath config cid = cacheFolder config ++ "/" ++ filenameFromCacheId cid


-- TODO pass cache config
runCache :: Members '[Embed IO] r => CacheConfig -> Sem (Cache : r) a -> Sem r a
runCache config = do
  interpret $ \case
      PutCache cid frame -> doPutCache config cid frame
      GetCache cid -> doGetCache config cid
        -- return $ Left "not implemented"
        -- use config to get the final path too
        -- let csvFilename = filenameFromCacheId cid
        -- rpcap <- embed $ loadRows csvFilename
        -- return Right rpcap
      IsValid cid -> isCacheValid config cid

-- | Mock cache
--
runMockCache :: Members '[Embed IO] r => CacheConfig -> Sem (Cache : r) a -> Sem r a
runMockCache config = do
  interpret $ \case
      PutCache cid frame -> return True
      GetCache cid -> return $ Left "Not in cache"
        -- return $ Left "not implemented"
        -- use config to get the final path too
        -- let csvFilename = filenameFromCacheId cid
        -- rpcap <- embed $ loadRows csvFilename
        -- return Right rpcap
      IsValid cid -> return False

-- first check if the file exists ?
doGetCache :: (Serialize a, Members '[Embed IO] r)
  => CacheConfig
  -> CacheId
  -> Sem r (Either String a)
doGetCache config cid = return $ Left "Not implemented yet"
  -- do
  -- -- res <- embed $ loadRows csvFilename
  -- -- exists <- embed $ fileExist csvFilename
  -- res <- embed $ CE.try @IOException $ loadRows csvFilename
  -- case res of
  --   Left _excpt -> return $ Left "Exception"
  --   Right x -> return (Right x)
  -- where
  --     csvFilename = getFullPath config cid


-- TODO reuse export function ?
doPutCache :: (Serialize a, Members '[Embed IO] r)
  => CacheConfig -> CacheId -> a -> Sem r Bool
doPutCache config cid resource =
  -- writeFile
  -- writeCSV :: (ColumnHeaders ts, Foldable f, RecordToList ts, RecMapMethod ShowCSV ElField ts) => FilePath -> f (Record ts) -> IO ()
  -- produceDSV
  -- embed $ writeCSV csvFilename frame >> return True
  embed $ do
    writeFile csvFilename $ encode resource
    return True
  where
      csvFilename = getFullPath config cid


-- TODO log ? / compare inputs date
isCacheValid :: Members '[Embed IO] r => CacheConfig -> CacheId -> Sem r Bool
isCacheValid config cid =
  embed $ doesFileExist filename
  where
    filename = getFullPath config cid