{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -fno-warn-missing-fields #-}
-- | A Shakespearean module for general text processing, introducing type-safe,
-- compile-time variable interpolation.
--
-- Text templates use the same parser as for other shakespearean templates
-- which enables variable interpolation using @#{..}@.  The parser also
-- recognize the @@{..}@ and @^{..}@ syntax.
--
-- If it is necessary that your template produces the output containing one of
-- the interpolation syntax you can escape the sequence using a backslash:
--
-- > λ> :set -XQuasiQuotes
-- > λ> let bar = 23 :: Int in [st|#{bar}|] :: Text
--
-- produces "23", but
--
-- > λ> let bar = 23 :: Int in [st|#\{bar}|] :: Text
--
-- returns "#{bar}".  The escaping backslash is removed from the output.
--
-- Further reading:
-- Shakespearean templates: <https://www.yesodweb.com/book/shakespearean-templates>
module Text.Shakespeare.Text
    ( TextUrl
    , ToText (..)
    , renderTextUrl
    , stext
    , stextFile
    , text
    , textFile
    , textFileDebug
    , textFileReload
    , st -- | strict text
    , lt -- | lazy text, same as stext :)
    , sbt -- | strict text whose left edge is aligned with bar ('|')
    , lbt -- | lazy text, whose left edge is aligned with bar ('|')
    -- * Yesod code generation
    , codegen
    , codegenSt
    , codegenFile
    , codegenFileReload
    ) where

import Language.Haskell.TH.Quote (QuasiQuoter (..))
import Language.Haskell.TH.Syntax
import Data.Text.Lazy.Builder (Builder, fromText, toLazyText, fromLazyText)
import Data.Text.Lazy.Builder.Int (decimal)
import qualified Data.Text as TS
import qualified Data.Text.Lazy as TL
import Text.Shakespeare
import Data.Int (Int32, Int64)

renderTextUrl :: RenderUrl url -> TextUrl url -> TL.Text
renderTextUrl :: forall url. RenderUrl url -> TextUrl url -> Text
renderTextUrl RenderUrl url
r TextUrl url
s = Builder -> Text
toLazyText (Builder -> Text) -> Builder -> Text
forall a b. (a -> b) -> a -> b
$ TextUrl url
s RenderUrl url
r

type TextUrl url = RenderUrl url -> Builder

class ToText a where
    toText :: a -> Builder
instance ToText Builder where toText :: Builder -> Builder
toText = Builder -> Builder
forall a. a -> a
id
instance ToText [Char ] where toText :: [Char] -> Builder
toText = Text -> Builder
fromLazyText (Text -> Builder) -> ([Char] -> Text) -> [Char] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Text
TL.pack
instance ToText TS.Text where toText :: Text -> Builder
toText = Text -> Builder
fromText
instance ToText TL.Text where toText :: Text -> Builder
toText = Text -> Builder
fromLazyText

instance ToText Int32 where toText :: Int32 -> Builder
toText = Int32 -> Builder
forall a. Integral a => a -> Builder
decimal
instance ToText Int64 where toText :: Int64 -> Builder
toText = Int64 -> Builder
forall a. Integral a => a -> Builder
decimal
instance ToText Int   where toText :: Int -> Builder
toText = Int -> Builder
forall a. Integral a => a -> Builder
decimal

settings :: Q ShakespeareSettings
settings :: Q ShakespeareSettings
settings = do
  Exp
toTExp <- [|toText|]
  Exp
wrapExp <- [|id|]
  Exp
unWrapExp <- [|id|]
  ShakespeareSettings -> Q ShakespeareSettings
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (ShakespeareSettings -> Q ShakespeareSettings)
-> ShakespeareSettings -> Q ShakespeareSettings
forall a b. (a -> b) -> a -> b
$ ShakespeareSettings
defaultShakespeareSettings { toBuilder :: Exp
toBuilder = Exp
toTExp
  , wrap :: Exp
wrap = Exp
wrapExp
  , unwrap :: Exp
unwrap = Exp
unWrapExp
  }

-- | "Simple text" quasi-quoter. May only be used to generate expressions.
--
-- Generated expressions have type 'TL.Text'.
--
-- @
-- >>> do let x = "world"
--        'Data.Text.Lazy.IO.putStrLn' ['stext'|Hello, #{x}!|]
-- Hello, world!
-- @
stext :: QuasiQuoter
stext :: QuasiQuoter
stext =
  QuasiQuoter { quoteExp :: [Char] -> Q Exp
quoteExp = \[Char]
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
settings
    Exp
render <- [|toLazyText|]
    Exp
rendered <- ShakespeareSettings -> [Char] -> Q Exp
shakespeareFromString ShakespeareSettings
rs { justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
s
    Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp
render Exp -> Exp -> Exp
`AppE` Exp
rendered)
    }

lt, st, text, lbt, sbt :: QuasiQuoter
lt :: QuasiQuoter
lt = QuasiQuoter
stext

st :: QuasiQuoter
st = 
  QuasiQuoter { quoteExp :: [Char] -> Q Exp
quoteExp = \[Char]
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
settings
    Exp
render <- [|TL.toStrict . toLazyText|]
    Exp
rendered <- ShakespeareSettings -> [Char] -> Q Exp
shakespeareFromString ShakespeareSettings
rs { justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
s
    Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp
render Exp -> Exp -> Exp
`AppE` Exp
rendered)
    }

text :: QuasiQuoter
text = QuasiQuoter { quoteExp :: [Char] -> Q Exp
quoteExp = \[Char]
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
settings
    QuasiQuoter -> [Char] -> Q Exp
quoteExp (ShakespeareSettings -> QuasiQuoter
shakespeare ShakespeareSettings
rs) ([Char] -> Q Exp) -> [Char] -> Q Exp
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> [Char] -> [Char]
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
'\r') [Char]
s
    }

dropBar :: [TL.Text] -> [TL.Text]
dropBar :: [Text] -> [Text]
dropBar [] = []
dropBar (Text
c:[Text]
cx) = Text
cText -> [Text] -> [Text]
forall a. a -> [a] -> [a]
:[Text] -> [Text]
dropBar' [Text]
cx
  where
    dropBar' :: [Text] -> [Text]
dropBar' [Text]
txt = [Text] -> [Text]
forall a. [a] -> [a]
reverse ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ Int -> [Text] -> [Text]
forall a. Int -> [a] -> [a]
drop Int
1 ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ (Text -> Text) -> [Text] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Int64 -> Text -> Text
TL.drop Int64
1 (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> Text -> Text
TL.dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'|')) ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Text] -> [Text]
forall a. [a] -> [a]
reverse [Text]
txt

lbt :: QuasiQuoter
lbt = 
  QuasiQuoter { quoteExp :: [Char] -> Q Exp
quoteExp = \[Char]
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
settings
    Exp
render <- [|TL.unlines . dropBar . TL.lines . toLazyText|]
    Exp
rendered <- ShakespeareSettings -> [Char] -> Q Exp
shakespeareFromString ShakespeareSettings
rs { justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
s
    Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp
render Exp -> Exp -> Exp
`AppE` Exp
rendered)
    }

sbt :: QuasiQuoter
sbt = 
  QuasiQuoter { quoteExp :: [Char] -> Q Exp
quoteExp = \[Char]
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
settings
    Exp
render <- [|TL.toStrict . TL.unlines . dropBar . TL.lines . toLazyText|]
    Exp
rendered <- ShakespeareSettings -> [Char] -> Q Exp
shakespeareFromString ShakespeareSettings
rs { justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
s
    Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp
render Exp -> Exp -> Exp
`AppE` Exp
rendered)
    }

textFile :: FilePath -> Q Exp
textFile :: [Char] -> Q Exp
textFile [Char]
fp = do
    ShakespeareSettings
rs <- Q ShakespeareSettings
settings
    ShakespeareSettings -> [Char] -> Q Exp
shakespeareFile ShakespeareSettings
rs [Char]
fp


textFileDebug :: FilePath -> Q Exp
textFileDebug :: [Char] -> Q Exp
textFileDebug = [Char] -> Q Exp
textFileReload
{-# DEPRECATED textFileDebug "Please use textFileReload instead" #-}

textFileReload :: FilePath -> Q Exp
textFileReload :: [Char] -> Q Exp
textFileReload [Char]
fp = do
    ShakespeareSettings
rs <- Q ShakespeareSettings
settings
    ShakespeareSettings -> [Char] -> Q Exp
shakespeareFileReload ShakespeareSettings
rs [Char]
fp

-- | Like 'stext', but reads an external file at compile-time.
--
-- @since 2.0.22
stextFile :: FilePath -> Q Exp
stextFile :: [Char] -> Q Exp
stextFile [Char]
fp = do
  ShakespeareSettings
rs <- Q ShakespeareSettings
settings
  [|toLazyText $(ShakespeareSettings -> [Char] -> Q Exp
shakespeareFile ShakespeareSettings
rs { justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
fp)|]

-- | codegen is designed for generating Yesod code, including templates
-- So it uses different interpolation characters that won't clash with templates.
codegenSettings :: Q ShakespeareSettings
codegenSettings :: Q ShakespeareSettings
codegenSettings = do
  Exp
toTExp <- [|toText|]
  Exp
wrapExp <- [|id|]
  Exp
unWrapExp <- [|id|]
  ShakespeareSettings -> Q ShakespeareSettings
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (ShakespeareSettings -> Q ShakespeareSettings)
-> ShakespeareSettings -> Q ShakespeareSettings
forall a b. (a -> b) -> a -> b
$ ShakespeareSettings
defaultShakespeareSettings { toBuilder :: Exp
toBuilder = Exp
toTExp
  , wrap :: Exp
wrap = Exp
wrapExp
  , unwrap :: Exp
unwrap = Exp
unWrapExp
  , varChar :: Char
varChar = Char
'~'
  , urlChar :: Char
urlChar = Char
'*'
  , intChar :: Char
intChar = Char
'&'
  , justVarInterpolation :: Bool
justVarInterpolation = Bool
True -- always!
  }

-- | codegen is designed for generating Yesod code, including templates
-- So it uses different interpolation characters that won't clash with templates.
-- You can use the normal text quasiquoters to generate code
codegen :: QuasiQuoter
codegen :: QuasiQuoter
codegen =
  QuasiQuoter { quoteExp :: [Char] -> Q Exp
quoteExp = \[Char]
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
codegenSettings
    Exp
render <- [|toLazyText|]
    Exp
rendered <- ShakespeareSettings -> [Char] -> Q Exp
shakespeareFromString ShakespeareSettings
rs { justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
s
    Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp
render Exp -> Exp -> Exp
`AppE` Exp
rendered)
    }

-- | Generates strict Text
-- codegen is designed for generating Yesod code, including templates
-- So it uses different interpolation characters that won't clash with templates.
codegenSt :: QuasiQuoter
codegenSt :: QuasiQuoter
codegenSt =
  QuasiQuoter { quoteExp :: [Char] -> Q Exp
quoteExp = \[Char]
s -> do
    ShakespeareSettings
rs <- Q ShakespeareSettings
codegenSettings
    Exp
render <- [|TL.toStrict . toLazyText|]
    Exp
rendered <- ShakespeareSettings -> [Char] -> Q Exp
shakespeareFromString ShakespeareSettings
rs { justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
s
    Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp
render Exp -> Exp -> Exp
`AppE` Exp
rendered)
    }

codegenFileReload :: FilePath -> Q Exp
codegenFileReload :: [Char] -> Q Exp
codegenFileReload [Char]
fp = do
    ShakespeareSettings
rs <- Q ShakespeareSettings
codegenSettings
    Exp
render <- [|TL.toStrict . toLazyText|]
    Exp
rendered <- ShakespeareSettings -> [Char] -> Q Exp
shakespeareFileReload ShakespeareSettings
rs{ justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
fp
    Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp
render Exp -> Exp -> Exp
`AppE` Exp
rendered)

codegenFile :: FilePath -> Q Exp
codegenFile :: [Char] -> Q Exp
codegenFile [Char]
fp = do
    ShakespeareSettings
rs <- Q ShakespeareSettings
codegenSettings
    Exp
render <- [|TL.toStrict . toLazyText|]
    Exp
rendered <- ShakespeareSettings -> [Char] -> Q Exp
shakespeareFile ShakespeareSettings
rs{ justVarInterpolation :: Bool
justVarInterpolation = Bool
True } [Char]
fp
    Exp -> Q Exp
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return (Exp
render Exp -> Exp -> Exp
`AppE` Exp
rendered)