packages feed

keid-core-0.1.2.0: src/Resource/Source.hs

module Resource.Source
  ( Source(..)
  , load
  , embedFile
  ) where

import RIO

import Data.FileEmbed qualified
import Data.Typeable
import GHC.Stack (withFrozenCallStack)
import Language.Haskell.TH.Syntax qualified as TH
import Resource.Compressed.Zstd qualified as Zstd
import RIO.ByteString qualified as ByteString
import RIO.FilePath (takeFileName, takeExtension)
import RIO.Text qualified as Text

data Source
  = Bytes     (Maybe Text) ByteString
  | BytesZstd (Maybe Text) (Zstd.Compressed ByteString)
  | File      (Maybe Text) FilePath

instance Show Source where
  show = \case
    Bytes mlabel _bs ->
      maybe "<buffer>" Text.unpack mlabel
    BytesZstd mlabel _zbs ->
      maybe "<zstd buffer>" Text.unpack mlabel
    File mlabel filePath ->
      maybe filePath Text.unpack mlabel

load
  :: forall a m env
  .  ( MonadIO m
     , MonadReader env m
     , HasLogFunc env
     , Typeable a
     , HasCallStack
     )
  => (ByteString -> m a)
  -> Source
  -> m a
load action = \case
  Bytes label !bytes -> do
    withFrozenCallStack . logDebug $
      case label of
        Nothing ->
          "Loading " <> displayShow (typeRep $ Proxy @a)
        Just someText ->
          "Loading " <> displayShow (typeRep $ Proxy @a) <> " from " <> display someText
    action bytes

  BytesZstd label !bytesZstd ->
    case Zstd.decompressBytes bytesZstd of
      Left zstdError ->
        throwIO zstdError
      Right !bytes ->
        load action $
          Bytes label bytes

  File label filePath -> do
    !bytes <- Zstd.fromFileWith pure (liftIO . ByteString.readFile) filePath
    load action $
      Bytes
        (label <|> Just (fromString filePath))
        bytes

embedFile :: FilePath -> TH.Q TH.Exp
embedFile filePath =
  case takeExtension filePath of
    ".zst" -> do
      bytesZstd <- Data.FileEmbed.embedFile filePath
      compressed <- [| Zstd.Compressed |]
      let bytesZstdExpr = compressed `TH.AppE` bytesZstd

      constr <- [| BytesZstd label |]
      pure $ constr `TH.AppE` bytesZstdExpr

    _ -> do
      bytes <- Data.FileEmbed.embedFile filePath
      constr <- [| Bytes label |]
      pure $ constr `TH.AppE` bytes
  where
    label = Just . mappend "embedded|" $ takeFileName filePath