packages feed

ema-0.1.0.0: src/Ema/Generate.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TypeApplications #-}

module Ema.Generate where

import Control.Exception (throw)
import Control.Monad.Logger
import Ema.Class
import Ema.Route (routeFile)
import System.Directory (copyFile, createDirectoryIfMissing, doesDirectoryExist, doesFileExist)
import System.FilePath (takeDirectory, (</>))
import System.FilePattern.Directory (getDirectoryFiles)

log :: MonadLogger m => LogLevel -> Text -> m ()
log = logWithoutLoc "Generate"

generate ::
  forall model route m.
  (MonadEma m, Ema model route) =>
  FilePath ->
  model ->
  (model -> route -> LByteString) ->
  m ()
generate dest model render = do
  unlessM (liftIO $ doesDirectoryExist dest) $ do
    error "Destination does not exist"
  let routes = staticRoutes model
  log LevelInfo $ "Writing " <> show (length routes) <> " routes"
  forM_ routes $ \r -> do
    let fp = dest </> routeFile @model r
    log LevelInfo $ toText $ "W " <> fp
    let !s = render model r
    liftIO $ do
      createDirectoryIfMissing True (takeDirectory fp)
      writeFileLBS fp s
  forM_ (staticAssets $ Proxy @route) $ \staticPath -> do
    copyDirRecursively staticPath dest

newtype StaticAssetMissing = StaticAssetMissing FilePath
  deriving (Show, Exception)

copyDirRecursively ::
  MonadEma m =>
  -- | Source file or directory relative to CWD that will be copied
  FilePath ->
  -- | Directory *under* which the source file/dir will be copied
  FilePath ->
  m ()
copyDirRecursively srcRel destParent =
  liftIO (doesFileExist srcRel) >>= \case
    True -> do
      let b = destParent </> srcRel
      log LevelInfo $ toText $ "C " <> b
      liftIO $ copyFile srcRel b
    False ->
      liftIO (doesDirectoryExist srcRel) >>= \case
        False ->
          throw $ StaticAssetMissing srcRel
        True -> do
          fs <- liftIO $ getDirectoryFiles srcRel ["**"]
          forM_ fs $ \fp -> do
            let a = srcRel </> fp
                b = destParent </> srcRel </> fp
            log LevelInfo $ toText $ "C " <> b
            liftIO $ do
              createDirectoryIfMissing True (takeDirectory b)
              copyFile a b