packages feed

headroom-0.4.2.0: src/Headroom/PostProcess.hs

{-# LANGUAGE AllowAmbiguousTypes   #-}
{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude     #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE RecordWildCards       #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE StrictData            #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE TypeFamilies          #-}

{-|
Module      : Headroom.PostProcess
Description : Support for /post-processors/
Copyright   : (c) 2019-2021 Vaclav Svejcar
License     : BSD-3-Clause
Maintainer  : vaclav.svejcar@gmail.com
Stability   : experimental
Portability : POSIX

/Post-processing functions/ are basically functions that allows to post-process
already rendered /license headers/. This is useful to perform some additional
operations such as some sort of text alignment, update some parts of the header,
etc.
-}

module Headroom.PostProcess
  ( postProcess
  , configuredPostProcess
  , postProcessHeader
    -- * Environment Data Types
  , ConfiguredEnv(..)
  , mkConfiguredEnv
  )
where

import           Headroom.Configuration.Types        ( CtPostProcessConfigs
                                                     , PostProcessConfig(..)
                                                     , PostProcessConfigs(..)
                                                     , UpdateCopyrightConfig(..)
                                                     )
import           Headroom.Data.Has                   ( Has(..) )
import           Headroom.Data.Lens                  ( suffixLenses
                                                     , suffixLensesFor
                                                     )
import           Headroom.PostProcess.Types          ( PostProcess(..) )
import           Headroom.PostProcess.UpdateCopyright
                                                     ( SelectedAuthors(..)
                                                     , UpdateCopyrightMode(..)
                                                     , updateCopyright
                                                     )
import           Headroom.Template                   ( Template(..) )
import           Headroom.Template.TemplateRef       ( TemplateRef(..) )
import           Headroom.Types                      ( CurrentYear(..) )
import           Headroom.Variables.Types            ( Variables(..) )
import           Lens.Micro                          ( traverseOf )
import           RIO


suffixLenses ''PostProcessConfigs
suffixLenses ''UpdateCopyrightConfig
suffixLensesFor ["ppcConfig"] ''PostProcessConfig


-- | Runs the /post-processing function/ using the given /environment/ and text
-- of rendered /license header/ as input.
postProcess :: PostProcess env
            -- ^ /post-processor/ to run
            -> env
            -- ^ environment value
            -> Text
            -- ^ text of rendered /license header/
            -> Text
            -- ^ processed text of /license header/
postProcess (PostProcess fn) env input = runReader (fn input) env


-- | Composition of various /post-processors/, which environment is
-- based on /YAML/ configuration and which can be enabled/disabled to fit
-- end user's needs.
configuredPostProcess :: (Has CurrentYear env, Has UpdateCopyrightMode env)
                      => CtPostProcessConfigs
                      -- ^ configuration of /post-processors/
                      -> PostProcess env
                      -- ^ composed /post-processor/
configuredPostProcess PostProcessConfigs {..} = mconcat
  [ifEnabled ppcsUpdateCopyright updateCopyright]
 where
  ifEnabled PostProcessConfig {..} fn | ppcEnabled = fn
                                      | otherwise  = mempty


-- | Takes already rendered /license header/ and post-process it based on the
-- given configuration.
postProcessHeader :: ConfiguredEnv
                  -- ^ configuration used to define post-processing behaviour
                  -> Text
                  -- ^ rendered text of /license header/
                  -> Text
                  -- ^ post-processed text of /license header/
postProcessHeader env =
  postProcess (configuredPostProcess (cePostProcessConfigs env)) env


-- | Environemnt data type for the composed /post-processor/
-- ('configuredPostProcess').
data ConfiguredEnv = ConfiguredEnv
  { ceCurrentYear         :: CurrentYear
  -- ^ current year
  , cePostProcessConfigs  :: CtPostProcessConfigs
  -- ^ configuration of /post-processor/
  , ceUpdateCopyrightMode :: UpdateCopyrightMode
  -- ^ mode used by the 'updateCopyright' /post-processor/
  }
  deriving (Eq, Show)

suffixLensesFor ["ceCurrentYear", "ceUpdateCopyrightMode"] ''ConfiguredEnv

instance Has CurrentYear ConfiguredEnv where
  hasLens = ceCurrentYearL

instance Has UpdateCopyrightMode ConfiguredEnv where
  hasLens = ceUpdateCopyrightModeL


-- | Constructor function for 'ConfiguredEnv' data type. This function takes
-- 'Variables' as argument, because it performs template compilation on
-- selected fields of 'CtPostProcessConfigs'.
mkConfiguredEnv :: forall a m
                 . (Template a, MonadThrow m)
                => CurrentYear
                -- ^ current year
                -> Variables
                -- ^ template variables
                -> CtPostProcessConfigs
                -- ^ configuration for /post-processors/
                -> m ConfiguredEnv
                -- ^ environment data type
mkConfiguredEnv ceCurrentYear vars configs = do
  cePostProcessConfigs <- compileTemplates @a vars configs
  let ceUpdateCopyrightMode = mode cePostProcessConfigs
  pure ConfiguredEnv { .. }
 where
  authorsL = ppcsUpdateCopyrightL . ppcConfigL . uccSelectedAuthorsL
  mode     = \configs' -> maybe UpdateAllAuthors
                                (UpdateSelectedAuthors . SelectedAuthors)
                                (configs' ^. authorsL)


------------------------------  PRIVATE FUNCTIONS  -----------------------------

compileTemplates :: forall a m
                  . (Template a, MonadThrow m)
                 => Variables
                 -> CtPostProcessConfigs
                 -> m CtPostProcessConfigs
compileTemplates vars configs = configs & traverseOf authorsL compileAuthors'
 where
  authorsL        = ppcsUpdateCopyrightL . ppcConfigL . uccSelectedAuthorsL
  compileAuthors' = mapM . mapM $ compileAuthor
  compileAuthor   = \author -> do
    parsed <- parseTemplate @a (InlineRef author) author
    renderTemplate vars parsed