{-|
Functions for compiling Project and File Templates
-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}
module ProjectForge.Compile (
  -- * Compiling project templates
  -- $compileTemplates
    compileFileTemplate
  , compileProjectTemplate

  -- ** Re-exports
  , module Text.Mustache.Type
) where

import           Control.Exception
import           Control.Monad
import           Control.Monad.IO.Class
import           Data.Bifunctor
import qualified Data.Set                     as Set
import           Data.Text
import           ProjectForge.ProjectTemplate
import           Text.Mustache.Compile
import           Text.Mustache.Type


{- $compileTemplates

Template compilation marshals @Data.Text.Text@ into @'Text.Mustache.Template'@s.
The utilities here extend the utilities provided by @Text.Mustache@
to create @'FileTemplate'@s and @'ProjectTemplate'@s.
-}

{-|
Create a @'FileTemplate'@ from a @(FilePath, Text)@ pair.
Both the @FilePath@ and @Text@ may contain mustache variables.

This function returns an error (within @'MonadIO'@) in the case that
@'Text.Mustache.compileMustacheText'@ fails to compile
either the 'fileNameTemplate' or 'fileContentsTemplate'.

For example, the following code:

@
compileFileTemplate ("{{prjId}}.md",  "{{prjId}}")
@

produces the following template:

@
MkFileTemplate
    { fileNameTemplate = Template
        { templateActual = PName
            { unPName = "Filename" }
        , templateCache = fromList
            [
                ( PName
                    { unPName = "Filename" }
                ,
                    [ EscapedVar
                        ( Key
                            { unKey = [ "prjId" ] }
                        )
                    , TextBlock ".md"
                    ]
                )
            ]
        }
    , fileContentsTemplate = Template
        { templateActual = PName
            { unPName = "Contents" }
        , templateCache = fromList
            [
                ( PName
                    { unPName = "Contents" }
                ,
                    [ EscapedVar
                        ( Key
                            { unKey = [ "prjId" ] }
                        )
                    ]
                )
            ]
        }
    }
@
-}
compileFileTemplate :: MonadIO m => (FilePath, Text) -> m FileTemplate
compileFileTemplate :: forall (m :: * -> *).
MonadIO m =>
(FilePath, Text) -> m FileTemplate
compileFileTemplate (FilePath, Text)
x =
   (Template, Template) -> FileTemplate
mkFileTemplate forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (forall {a} {b}.
(Either (ParseErrorBundle Text Void) a,
 Either (ParseErrorBundle Text Void) b)
-> m (a, b)
mkTemplates forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap
    (PName -> Text -> Either (ParseErrorBundle Text Void) Template
compileMustacheText PName
"Filename" forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Text
pack)
    (PName -> Text -> Either (ParseErrorBundle Text Void) Template
compileMustacheText PName
"Contents") forall a b. (a -> b) -> a -> b
$ (FilePath, Text)
x)
  where
    mkTemplates :: (Either (ParseErrorBundle Text Void) a,
 Either (ParseErrorBundle Text Void) b)
-> m (a, b)
mkTemplates  = \case
      (Right a
fn, Right b
ctnts) -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (a
fn, b
ctnts)
      (Left ParseErrorBundle Text Void
e, Either (ParseErrorBundle Text Void) b
_)             -> forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall e a. Exception e => e -> IO a
throwIO forall a b. (a -> b) -> a -> b
$ ParseErrorBundle Text Void -> MustacheException
MustacheParserException ParseErrorBundle Text Void
e
      (Either (ParseErrorBundle Text Void) a
_ , Left ParseErrorBundle Text Void
e)            -> forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall e a. Exception e => e -> IO a
throwIO forall a b. (a -> b) -> a -> b
$ ParseErrorBundle Text Void -> MustacheException
MustacheParserException ParseErrorBundle Text Void
e
    mkFileTemplate :: (Template, Template) -> FileTemplate
mkFileTemplate = forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry forall a b. (a -> b) -> a -> b
$ FilePath -> Template -> Template -> FileTemplate
MkFileTemplate (forall a b. (a, b) -> a
fst (FilePath, Text)
x)


{-|
Generate all @'FileTemplate'@s for a @'ProjectTemplate'@.
-}
compileProjectTemplate :: MonadIO m => [(FilePath, Text)] -> m ProjectTemplate
compileProjectTemplate :: forall (m :: * -> *).
MonadIO m =>
[(FilePath, Text)] -> m ProjectTemplate
compileProjectTemplate [(FilePath, Text)]
x =
    Set FileTemplate -> ProjectTemplate
MkProjectTemplate forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Ord a => [a] -> Set a
Set.fromList forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse forall (m :: * -> *).
MonadIO m =>
(FilePath, Text) -> m FileTemplate
compileFileTemplate [(FilePath, Text)]
x