{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
module Text.Mustache.Internal.Types where
import Control.Arrow
import Control.Monad.RWS hiding (lift)
import qualified Data.Aeson as Aeson
import Data.Foldable (toList)
import qualified Data.HashMap.Strict as HM
import qualified Data.HashSet as HS
import qualified Data.Map as Map
import Data.Scientific
import qualified Data.Sequence as Seq
import qualified Data.Set as Set
import Data.Text
import qualified Data.Text.Lazy as LT
import qualified Data.Vector as V
import Language.Haskell.TH.Lift (Lift (lift), deriveLift)
data SubstitutionError
= VariableNotFound [Key]
| InvalidImplicitSectionContextType String
| InvertedImplicitSection
| SectionTargetNotFound [Key]
| PartialNotFound FilePath
| DirectlyRenderedValue Value
deriving (Show)
tellError :: SubstitutionError -> SubM ()
tellError e = SubM $ tell ([e], [])
tellSuccess :: Text -> SubM ()
tellSuccess s = SubM $ tell ([], [s])
newtype SubM a = SubM { runSubM' :: RWS (Context Value, TemplateCache) ([SubstitutionError], [Text]) () a } deriving (Monad, Functor, Applicative, MonadReader (Context Value, TemplateCache))
runSubM :: SubM a -> Context Value -> TemplateCache -> ([SubstitutionError], [Text])
runSubM comp ctx cache = snd $ evalRWS (runSubM' comp) (ctx, cache) ()
shiftContext :: Context Value -> SubM a -> SubM a
shiftContext = local . first . const
search :: [Key] -> SubM (Maybe Value)
search [] = return Nothing
search (key:nextKeys) = (>>= innerSearch nextKeys) <$> go
where
go = asks fst >>= \case
Context parents focus -> do
let searchParents = case parents of
(newFocus: newParents) -> shiftContext (Context newParents newFocus) $ go
_ -> return Nothing
case focus of
Object o ->
case HM.lookup key o of
Just res -> return $ Just res
_ -> searchParents
_ -> searchParents
innerSearch :: [Key] -> Value -> Maybe Value
innerSearch [] v = Just v
innerSearch (y:ys) (Object o) = HM.lookup y o >>= innerSearch ys
innerSearch _ _ = Nothing
type STree = ASTree Text
type ASTree α = [Node α]
data Node α
= TextBlock α
| Section DataIdentifier (ASTree α)
| InvertedSection DataIdentifier (ASTree α)
| Variable Bool DataIdentifier
| Partial (Maybe α) FilePath
deriving (Show, Eq)
data DataIdentifier
= NamedData [Key]
| Implicit
deriving (Show, Eq)
type Array = V.Vector Value
type Object = HM.HashMap Text Value
type Pair = (Text, Value)
data Context α = Context { ctxtParents :: [α], ctxtFocus :: α }
deriving (Eq, Show, Ord)
data Value
= Object !Object
| Array !Array
| Number !Scientific
| String !Text
| Lambda (STree -> SubM STree)
| Bool !Bool
| Null
instance Show Value where
show (Lambda _) = "Lambda function"
show (Object o) = show o
show (Array a) = show a
show (String s) = show s
show (Number n) = show n
show (Bool b) = show b
show Null = "null"
listToMustache' :: ToMustache ω => [ω] -> Value
listToMustache' = Array . V.fromList . fmap toMustache
class ToMustache ω where
toMustache :: ω -> Value
listToMustache :: [ω] -> Value
listToMustache = listToMustache'
instance ToMustache Float where
toMustache = Number . fromFloatDigits
instance ToMustache Double where
toMustache = Number . fromFloatDigits
instance ToMustache Integer where
toMustache = Number . fromInteger
instance ToMustache Int where
toMustache = toMustache . toInteger
instance ToMustache Char where
toMustache = toMustache . (:[])
listToMustache = String . pack
instance ToMustache Value where
toMustache = id
instance ToMustache Bool where
toMustache = Bool
instance ToMustache () where
toMustache = const Null
instance ToMustache ω => ToMustache (Maybe ω) where
toMustache (Just w) = toMustache w
toMustache Nothing = Null
instance ToMustache Text where
toMustache = String
instance ToMustache LT.Text where
toMustache = String . LT.toStrict
instance ToMustache Scientific where
toMustache = Number
instance ToMustache α => ToMustache [α] where
toMustache = listToMustache
instance ToMustache ω => ToMustache (Seq.Seq ω) where
toMustache = listToMustache' . toList
instance ToMustache ω => ToMustache (V.Vector ω) where
toMustache = Array . fmap toMustache
instance (ToMustache ω) => ToMustache (Map.Map Text ω) where
toMustache = mapInstanceHelper id
instance (ToMustache ω) => ToMustache (Map.Map LT.Text ω) where
toMustache = mapInstanceHelper LT.toStrict
instance (ToMustache ω) => ToMustache (Map.Map String ω) where
toMustache = mapInstanceHelper pack
mapInstanceHelper :: ToMustache v => (a -> Text) -> Map.Map a v -> Value
mapInstanceHelper conv =
toMustache
. Map.foldrWithKey
(\k -> HM.insert (conv k) . toMustache)
HM.empty
instance ToMustache ω => ToMustache (HM.HashMap Text ω) where
toMustache = Object . fmap toMustache
instance ToMustache ω => ToMustache (HM.HashMap LT.Text ω) where
toMustache = hashMapInstanceHelper LT.toStrict
instance ToMustache ω => ToMustache (HM.HashMap String ω) where
toMustache = hashMapInstanceHelper pack
hashMapInstanceHelper :: ToMustache v => (a -> Text) -> HM.HashMap a v -> Value
hashMapInstanceHelper conv =
toMustache
. HM.foldrWithKey
(\k -> HM.insert (conv k) . toMustache)
HM.empty
instance ToMustache (STree -> SubM STree) where
toMustache = Lambda
instance ToMustache Aeson.Value where
toMustache (Aeson.Object o) = Object $ fmap toMustache o
toMustache (Aeson.Array a) = Array $ fmap toMustache a
toMustache (Aeson.Number n) = Number n
toMustache (Aeson.String s) = String s
toMustache (Aeson.Bool b) = Bool b
toMustache Aeson.Null = Null
instance ToMustache ω => ToMustache (HS.HashSet ω) where
toMustache = listToMustache' . HS.toList
instance ToMustache ω => ToMustache (Set.Set ω) where
toMustache = listToMustache' . Set.toList
instance (ToMustache α, ToMustache β) => ToMustache (α, β) where
toMustache (a, b) = toMustache [toMustache a, toMustache b]
instance (ToMustache α, ToMustache β, ToMustache γ)
=> ToMustache (α, β, γ) where
toMustache (a, b, c) = toMustache [toMustache a, toMustache b, toMustache c]
instance (ToMustache α, ToMustache β, ToMustache γ, ToMustache δ)
=> ToMustache (α, β, γ, δ) where
toMustache (a, b, c, d) = toMustache
[ toMustache a
, toMustache b
, toMustache c
, toMustache d
]
instance ( ToMustache α
, ToMustache β
, ToMustache γ
, ToMustache δ
, ToMustache ε
) => ToMustache (α, β, γ, δ, ε) where
toMustache (a, b, c, d, e) = toMustache
[ toMustache a
, toMustache b
, toMustache c
, toMustache d
, toMustache e
]
instance ( ToMustache α
, ToMustache β
, ToMustache γ
, ToMustache δ
, ToMustache ε
, ToMustache ζ
) => ToMustache (α, β, γ, δ, ε, ζ) where
toMustache (a, b, c, d, e, f) = toMustache
[ toMustache a
, toMustache b
, toMustache c
, toMustache d
, toMustache e
, toMustache f
]
instance ( ToMustache α
, ToMustache β
, ToMustache γ
, ToMustache δ
, ToMustache ε
, ToMustache ζ
, ToMustache η
) => ToMustache (α, β, γ, δ, ε, ζ, η) where
toMustache (a, b, c, d, e, f, g) = toMustache
[ toMustache a
, toMustache b
, toMustache c
, toMustache d
, toMustache e
, toMustache f
, toMustache g
]
instance ( ToMustache α
, ToMustache β
, ToMustache γ
, ToMustache δ
, ToMustache ε
, ToMustache ζ
, ToMustache η
, ToMustache θ
) => ToMustache (α, β, γ, δ, ε, ζ, η, θ) where
toMustache (a, b, c, d, e, f, g, h) = toMustache
[ toMustache a
, toMustache b
, toMustache c
, toMustache d
, toMustache e
, toMustache f
, toMustache g
, toMustache h
]
type TemplateCache = HM.HashMap String Template
type Key = Text
data Template = Template
{ name :: String
, ast :: STree
, partials :: TemplateCache
} deriving (Show)
instance Lift TemplateCache where
lift m = [| HM.fromList $(lift $ HM.toList m) |]
#if !MIN_VERSION_text(1,2,4)
instance Lift Text where
lift = lift . unpack
#endif
deriveLift ''DataIdentifier
deriveLift ''Node
deriveLift ''Template