packages feed

porcupine-core-0.1.0.0: src/Data/Locations/FunflowRemoteCache.hs

{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}

module Data.Locations.FunflowRemoteCache
  ( locationCacher
  , LiftCacher(..)
  ) where

import           Control.Exception.Safe
import           Control.Funflow.ContentHashable (ContentHash, hashToPath)
import qualified Control.Funflow.RemoteCache     as Remote
import           Control.Lens
import           Control.Monad.Trans
import           Data.Bifunctor                  (first)
import           Data.Locations.Accessors
import           Data.Locations.Loc
import           Katip
import           Path                            (toFilePath)
import           System.FilePath                 (dropTrailingPathSeparator)


hashToFilePath :: ContentHash -> FilePath
hashToFilePath = dropTrailingPathSeparator . toFilePath . hashToPath

newtype LocationCacher m = LocationCacher (SomeLoc m)

tryS :: (MonadCatch m) => m a -> m (Either String a)
tryS = fmap (over _Left (displayException :: SomeException -> String)) . try

instance (KatipContext m)
      => Remote.Cacher m (LocationCacher m) where
  push (LocationCacher (SomeGLoc rootLoc)) = Remote.pushAsArchive aliasPath $ \hash body -> do
    let loc = rootLoc `addSubdirToLoc` hashToFilePath hash
    katipAddNamespace "remoteCacher" $ logFM DebugS $ logStr $
      "Writing to file " ++ show loc
    writeLazyByte loc body
    pure Remote.PushOK
    where
      aliasPath from_ to_ = first show <$> tryS
        (copy
          (rootLoc `addSubdirToLoc` hashToFilePath from_)
          (rootLoc `addSubdirToLoc` hashToFilePath to_))
  pull (LocationCacher (SomeGLoc rootLoc)) = Remote.pullAsArchive $ \hash ->
    katipAddNamespace "remoteCacher" $ do
      let loc = rootLoc `addSubdirToLoc` hashToFilePath hash
      readResult <- tryS $ readLazyByte loc
      case readResult of
        Right bs -> do
          logFM DebugS $ logStr $ "Found in remote cache " ++ show loc
          return $ Remote.PullOK bs
        Left err -> do
          katipAddContext (sl "errorFromRemoteCache" err) $
            logFM DebugS $ logStr $ "Not in remote cache " ++ show loc
          return $ Remote.PullError err

locationCacher :: Maybe (SomeLoc m) -> Maybe (LocationCacher m)
locationCacher = fmap LocationCacher

newtype LiftCacher cacher = LiftCacher cacher

instance (MonadTrans t, Remote.Cacher m cacher, Monad (t m)) =>
         Remote.Cacher (t m) (LiftCacher cacher) where
  push (LiftCacher c) hash hash2 path = lift $ Remote.push c hash hash2 path
  pull (LiftCacher c) hash path = lift $ Remote.pull c hash path