{-| Module : $Header$ Description : Types and conversions Copyright : (c) Justus Adam, 2015 License : LGPL-3 Maintainer : development@justusadam.com Stability : experimental Portability : POSIX -} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE UnicodeSyntax #-} {-# LANGUAGE FlexibleContexts #-} module Text.Mustache.Types ( -- * Types for the Parser / Template MustacheAST , MustacheTemplate(..) , MustacheNode(..) -- * Types for the Substitution / Data , Value(..) -- ** Converting , object , (~>), (~=), (~~>), (~~=) , ToMustache, toMustache, toMustacheText, mFromJSON -- ** Representation , Array, Object , Context(..) ) where import qualified Data.Aeson as Aeson import Data.Functor ((<$>)) import Data.HashMap.Strict as HM import Data.Scientific import Data.Text import qualified Data.Text.Lazy as LT import qualified Data.Vector as V import Conversion import Conversion.Text () -- | Abstract syntax tree for a mustache template type MustacheAST = [MustacheNode Text] -- | Basic values composing the AST data MustacheNode a = MustacheText a | MustacheSection [Text] MustacheAST | MustacheInvertedSection [Text] MustacheAST | MustacheVariable Bool [Text] | MustachePartial FilePath deriving (Show, Eq) type Array = V.Vector Value type Object = HM.HashMap Text Value type KeyValuePair = (Text, Value) -- | Representation of stateful context for the substitution process data Context a = Context [a] a -- | Internal value AST data Value = Object Object | Array Array | Number Scientific | String Text | Lambda (Context Value → MustacheAST → Either String MustacheAST) | Bool Bool | Null instance Show Value where show (Lambda _) = "Lambda Context Value → MustacheAST → Either String MustacheAST" 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" -- | Conversion class class ToMustache a where toMustache ∷ a → Value instance ToMustache [Char] where toMustache = String . pack instance ToMustache Bool where toMustache = Bool instance ToMustache Char where toMustache = String . pack . return instance ToMustache () where toMustache = const Null instance ToMustache Text where toMustache = String instance ToMustache LT.Text where toMustache = String . LT.toStrict instance ToMustache Scientific where toMustache = Number instance ToMustache Value where toMustache = id instance ToMustache m ⇒ ToMustache [m] where toMustache = Array . V.fromList . fmap toMustache instance ToMustache m ⇒ ToMustache (V.Vector m) where toMustache = Array . fmap toMustache instance ToMustache m ⇒ ToMustache (HM.HashMap Text m) where toMustache = Object . fmap toMustache instance ToMustache (Context Value → MustacheAST → Either String MustacheAST) where toMustache = Lambda instance ToMustache (Context Value → MustacheAST → Either String Text) where toMustache f = Lambda wrapper where wrapper c lAST = return . MustacheText <$> f c lAST instance ToMustache (Context Value → MustacheAST → MustacheAST) where toMustache f = Lambda wrapper where wrapper c = Right . f c instance ToMustache (Context Value → MustacheAST → Text) where toMustache f = Lambda wrapper where wrapper c = Right . return . MustacheText . f c instance ToMustache (Context Value → MustacheAST → String) where toMustache f = toMustache wrapper where wrapper c = pack . f c instance ToMustache (MustacheAST → Either String MustacheAST) where toMustache f = Lambda $ const f instance ToMustache (MustacheAST → Either String Text) where toMustache f = Lambda wrapper where wrapper _ = fmap (return . MustacheText) . f instance ToMustache (MustacheAST → Either String String) where toMustache f = toMustache (fmap pack . f) instance ToMustache (MustacheAST → Text) where toMustache f = toMustache (Right . f ∷ MustacheAST -> Either String Text) instance ToMustache (MustacheAST → String) where toMustache f = toMustache (pack . f) 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 -- | Convenience function for creating Object values. -- -- This function is supposed to be used in conjuction with the '~>' and '~=' operators. -- -- ==== __Examples__ -- -- @ -- data Address = Address { ... } -- -- instance Address ToJSON where -- ... -- -- data Person = Person { name :: String, address :: Address } -- -- instance ToMustache Person where -- toMustache (Person { name, address }) = object -- [ "name" ~> name -- , "address" ~= address -- ] -- @ -- -- Here we can see that we can use the '~>' operator for values that have themselves -- a 'ToMustache' instance, or alternatively if they lack such an instance but provide -- an instance for the 'ToJSON' typeclass we can use the '~=' operator. object ∷ [(Text, Value)] → Value object = Object . HM.fromList -- | Map keys to values that provide a 'ToMustache' instance -- -- Recommended in conjunction with the `OverloadedStrings` extension. (~>) ∷ ToMustache m ⇒ Text → m → KeyValuePair (~>) t = (t, ) . toMustache -- | Map keys to values that provide a 'ToJSON' instance -- -- Recommended in conjunction with the `OverloadedStrings` extension. (~=) ∷ Aeson.ToJSON j ⇒ Text → j → KeyValuePair (~=) t = (t ~>) . Aeson.toJSON -- | Conceptually similar to '~>' but uses arbitrary String-likes as keys. (~~>) ∷ (Conversion t Text, ToMustache m) ⇒ t → m → KeyValuePair (~~>) = (~>) . convert -- | Conceptually similar to '~=' but uses arbitrary String-likes as keys. (~~=) ∷ (Conversion t Text, Aeson.ToJSON j) ⇒ t → j → KeyValuePair (~~=) = (~=) . convert -- | Converts arbitrary String-likes to Values toMustacheText ∷ Conversion t Text ⇒ t → Value toMustacheText = String . convert -- | Converts a value that can be represented as JSON to a Value. mFromJSON ∷ Aeson.ToJSON j ⇒ j → Value mFromJSON = toMustache . Aeson.toJSON {-| A compiled Template with metadata. -} data MustacheTemplate = MustacheTemplate { name ∷ String , ast ∷ MustacheAST , partials ∷ [MustacheTemplate] } deriving (Show)