packages feed

ginger2-2.0.0.0: test/Language/Ginger/TestUtils.hs

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

module Language.Ginger.TestUtils
where

import Control.Monad.Identity
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Maybe (listToMaybe)
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Map.Strict as Map
import Test.Tasty.QuickCheck

import Language.Ginger.Interpret
import Language.Ginger.Value
import Language.Ginger.AST

newtype ArbitraryText = ArbitraryText { unArbitraryText :: Text }
  deriving (Eq, Ord)

instance Show ArbitraryText where
  show (ArbitraryText t) = show t

instance Arbitrary ArbitraryText where
  arbitrary = ArbitraryText . Text.pack <$> listOf arbitrary

instance Monad m => ToValue ArbitraryText m where
  toValue (ArbitraryText t) = toValue t

newtype NonEmptyText = NonEmptyText Text
  deriving (Eq, Ord)

instance Show NonEmptyText where
  show (NonEmptyText t) = show t

instance Arbitrary NonEmptyText where
  arbitrary = NonEmptyText . Text.pack <$> listOf1 arbitrary

instance Monad m => ToValue NonEmptyText m where
  toValue (NonEmptyText t) = toValue t

newtype ArbitraryByteString = ArbitraryByteString ByteString
  deriving (Eq, Ord)

instance Show ArbitraryByteString where
  show (ArbitraryByteString t) = show t

instance Arbitrary ArbitraryByteString where
  arbitrary = ArbitraryByteString . BS.pack <$> listOf arbitrary

instance Monad m => ToValue ArbitraryByteString m where
  toValue (ArbitraryByteString t) = toValue t

newtype PositiveInt i = PositiveInt i
  deriving (Eq, Ord)

instance Show i => Show (PositiveInt i) where
  show (PositiveInt t) = show t

instance (Arbitrary i, Integral i) => Arbitrary (PositiveInt i) where
  arbitrary = do
    s <- getSize
    PositiveInt . fromInteger <$> chooseInteger (1, fromIntegral $ max 1 s)
  shrink (PositiveInt i) = map PositiveInt . filter (> 0) $ shrink i

instance (Monad m, ToValue i m) => ToValue (PositiveInt i) m where
  toValue (PositiveInt t) = toValue t

safeAt :: Int -> [a] -> Maybe a
safeAt n = listToMaybe . drop n

justNonzero :: (Eq a, Num a) => a -> Maybe a
justNonzero 0 = Nothing
justNonzero n = Just n

justPositive :: (Eq a, Ord a, Num a) => a -> Maybe a
justPositive n | n > 0 = Just n
justPositive _ = Nothing  

leftPRE :: RuntimeError -> Either PrettyRuntimeError a
leftPRE = Left . PrettyRuntimeError

mapLeft :: (a -> b) -> Either a c -> Either b c
mapLeft f (Left x) = Left (f x)
mapLeft _ (Right x) = Right x

mapRight :: (b -> c) -> Either a b -> Either a c
mapRight _ (Left x) = Left x
mapRight f (Right x) = Right (f x)

newtype PrettyRuntimeError = PrettyRuntimeError RuntimeError
  deriving (Eq)

instance Show PrettyRuntimeError where
  show (PrettyRuntimeError (TemplateParseError _ err)) = Text.unpack err
  show (PrettyRuntimeError e) = show e

runGingerIdentity :: GingerT Identity a -> a
runGingerIdentity action =
  either (error . show) id $ runGingerIdentityEither action

runGingerIdentityEither :: GingerT Identity a -> Either PrettyRuntimeError a
runGingerIdentityEither action =
  mapLeft (PrettyRuntimeError . unPositionedError) $
    runIdentity (runGingerT action defContext defEnv)

runGingerIdentityWithLoader :: TemplateLoader Identity
                                  -> GingerT Identity a
                                  -> a
runGingerIdentityWithLoader loader action =
  either (error . show) id $ runGingerIdentityEitherWithLoader loader action

runGingerIdentityEitherWithLoader :: TemplateLoader Identity
                                  -> GingerT Identity a
                                  -> Either PrettyRuntimeError a
runGingerIdentityEitherWithLoader loader action =
  mapLeft (PrettyRuntimeError . unPositionedError) $
    runIdentity (runGingerT action defContext { contextLoadTemplateFile = loader } defEnv)

mockLoader :: [(Text, Text)] -> TemplateLoader Identity
mockLoader entries name =
  pure $ Map.lookup name tpls
  where
    tpls = Map.fromList entries

unPositionedS :: Statement -> Statement
unPositionedS = traverseS go unPositionedE
  where
    go (PositionedS _ s) = unPositionedS s
    go s = s

unPositionedE :: Expr -> Expr
unPositionedE = traverseE go unPositionedS
  where
    go (PositionedE _ e) = unPositionedE e
    go e = e

unPositionedError :: RuntimeError -> RuntimeError
unPositionedError (PositionedError _ e) = unPositionedError e
unPositionedError e = e