inferno-vc-0.1.0: src/Inferno/VersionControl/Client/Cached.hs
module Inferno.VersionControl.Client.Cached where
import Control.Monad (forM, forM_)
import Control.Monad.Error.Lens (throwing)
import Control.Monad.Except (MonadError (..))
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Reader (MonadReader (..))
import Data.Aeson (FromJSON, ToJSON, encode)
import qualified Data.ByteString.Lazy as BL
import Data.Either (partitionEithers)
import Data.Generics.Product (HasType, getTyped)
import Data.Generics.Sum (AsType (..))
import qualified Data.Map as Map
import GHC.Generics (Generic)
import qualified Inferno.VersionControl.Client as VCClient
import Inferno.VersionControl.Log (VCServerTrace)
import qualified Inferno.VersionControl.Operations as Ops
import qualified Inferno.VersionControl.Operations.Error as Ops
import Inferno.VersionControl.Server (VCServerError)
import Inferno.VersionControl.Types
( VCMeta,
VCObject,
VCObjectHash,
vcObjectHashToByteString,
)
import Plow.Logging (IOTracer)
import Servant.Client (ClientEnv, ClientError)
import Servant.Typed.Error (TypedClientM, runTypedClientM)
import System.Directory (doesFileExist)
import System.FilePath.Posix ((</>))
data CachedVCClientError
= ClientVCStoreError VCServerError
| ClientServantError ClientError
| LocalVCStoreError Ops.VCStoreError
deriving (Show, Generic)
liftServantClient ::
( MonadError e m,
MonadIO m,
MonadReader s m,
HasType ClientEnv s,
AsType a e,
AsType ClientError e
) =>
TypedClientM a b ->
m b
liftServantClient m = do
client <- getTyped <$> ask
(liftIO $ runTypedClientM m client) >>= \case
Left (Left clientErr) -> throwing _Typed $ clientErr
Left (Right serverErr) -> throwing _Typed $ serverErr
Right res -> pure res
fetchVCObjectClosure ::
( AsType VCServerError err,
AsType ClientError err,
AsType Ops.VCStoreError err,
MonadError err m,
HasType (IOTracer VCServerTrace) env,
HasType Ops.VCStorePath env,
HasType ClientEnv env,
MonadReader env m,
MonadIO m,
FromJSON a,
FromJSON g,
ToJSON a,
ToJSON g
) =>
([VCObjectHash] -> VCClient.ClientMWithVCStoreError (Map.Map VCObjectHash (VCMeta a g VCObject))) ->
(VCObjectHash -> VCClient.ClientMWithVCStoreError [VCObjectHash]) ->
VCObjectHash ->
m (Map.Map VCObjectHash (VCMeta a g VCObject))
fetchVCObjectClosure fetchVCObjects fetchVCObjectClosureHashes objHash = do
Ops.VCStorePath storePath <- getTyped <$> ask
deps <-
(liftIO $ doesFileExist $ storePath </> "deps" </> show objHash) >>= \case
False -> do
deps <- liftServantClient $ fetchVCObjectClosureHashes objHash
Ops.writeBS
(storePath </> "deps" </> show objHash)
$ BL.concat [BL.fromStrict (vcObjectHashToByteString h) <> "\n" | h <- deps]
pure deps
True -> Ops.fetchVCObjectClosureHashes objHash
(nonLocalHashes, localHashes) <-
partitionEithers
<$> ( forM (objHash : deps) $ \depHash -> do
(liftIO $ doesFileExist $ storePath </> show depHash) >>= \case
True -> pure $ Right depHash
False -> pure $ Left depHash
)
localObjs <-
Map.fromList
<$> ( forM localHashes $ \h ->
(h,) <$> Ops.fetchVCObject h
)
nonLocalObjs <- liftServantClient $ fetchVCObjects nonLocalHashes
forM_ (Map.toList nonLocalObjs) $ \(h, o) ->
liftIO $ BL.writeFile (storePath </> show h) $ encode o
pure $ localObjs `Map.union` nonLocalObjs