hierarchical-env-0.2.0.0: test/Control/Env/Hierarchical/InternalSpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Control.Env.Hierarchical.InternalSpec where
import Control.Env.Hierarchical.Internal
( Environment (Fields, Fields1, superL),
Extends (Extends),
Field (fieldL),
Has,
Root (Root),
Super,
getL,
runIF,
)
import Control.Method (Interface (IBase), mapBaseRIO)
import GHC.Generics (Generic)
import Lens.Micro.Mtl (view)
import Lens.Micro.TH (makeLenses)
import RIO (RIO, runRIO)
import Test.Hspec (Spec, describe, it, shouldBe)
data Obj1 env = Obj1
{ _method1 :: RIO env Int,
_method2 :: RIO env Int
}
makeLenses ''Obj1
data Obj2 env = Obj2
{ _method3 :: RIO env Int,
_method4 :: RIO env Int
}
makeLenses ''Obj2
newtype Param1 = Param1 Int
deriving (Eq, Show)
newtype Param2 = Param2 Int
deriving (Eq, Show)
newtype Param3 = Param3 Int
deriving (Eq, Show)
data Env1 = Env1
{ _obj1 :: Obj1 Env1,
_param1 :: Param1
}
makeLenses ''Env1
data Env2 = Env2
{ _env1 :: Extends Env1,
_obj2 :: Obj2 Env2,
_param2 :: Param2
}
makeLenses ''Env2
instance Environment Env1 where
type Fields1 Env1 = '[Obj1]
type Fields Env1 = '[Env1, Obj1 Env1, Param1]
type Super Env1 = Root
superL f x = x <$ f Root
instance Environment Env2 where
type Fields1 Env2 = '[Obj2]
type Fields Env2 = '[Extends Env1, Obj2 Env2, Param2]
type Super Env2 = Env1
instance Field (Obj1 Env1) Env1 where
fieldL = obj1
instance Field Param1 Env1 where
fieldL = param1
instance Field (Extends Env1) Env2 where
fieldL = env1
instance Field (Obj2 Env2) Env2 where
fieldL = obj2
instance Field Param2 Env2 where
fieldL = param2
env1Impl :: Env1
env1Impl =
Env1
{ _obj1 =
Obj1
{ _method1 = pure 1,
_method2 = pure 2
},
_param1 = Param1 1
}
env2Impl :: Env2
env2Impl =
Env2
{ _obj2 =
Obj2
{ _method3 = do
x <- runIF _method1
y <- runIF _method2
pure $ x + y,
_method4 = pure 4
},
_param2 = Param2 2,
_env1 = Extends env1Impl
}
data Env3 env = Env3 Param3 (Extends env)
instance Environment (Env3 env) where
type Fields (Env3 env) = '[Param3]
type Fields1 (Env3 env) = '[]
type Super (Env3 env) = env
instance Field (Extends env) (Env3 env) where
fieldL f (Env3 x1 x2) = fmap (\y2 -> Env3 x1 y2) (f x2)
instance Field Param3 (Env3 env) where
fieldL f (Env3 x1 x2) = fmap (\y1 -> Env3 y1 x2) (f x1)
newtype Obj3 env = Obj3 {_runObj3 :: RIO env Int}
deriving (Generic)
instance Interface Obj3 where
type IBase Obj3 = RIO
obj3Impl :: Has Param1 env => Obj3 env
obj3Impl = mapBaseRIO (Env3 (Param3 3) . Extends) $
Obj3 $ do
Param3 n <- view getL
Param1 m <- view getL
pure $ n + m
spec :: Spec
spec = do
describe "getL" $ do
it "getL @(Obj1 Env1) from Env1" $ do
n <- runRIO env1Impl $ do
x <- view (getL @(Obj1 Env1))
_method1 x
n `shouldBe` 1
it "getL @Param1 from Env1" $ do
n <- runRIO env1Impl $ do
view (getL @Param1)
n `shouldBe` Param1 1
it "getL @(Obj1 Env1) from Env2" $ do
n <- runRIO env2Impl $ do
x <- view (getL @(Obj1 Env1))
env <- view (getL @Env1)
runRIO env $ _method1 x
n `shouldBe` 1
it "getL @Param1 from Env1" $ do
n <- runRIO env2Impl $ do
view (getL @Param1)
n `shouldBe` Param1 1
it "getL @(Obj2 Env2) from Env2" $ do
n <- runRIO env2Impl $ do
x <- view (getL @(Obj2 Env2))
_method3 x
n `shouldBe` 3
it "getL @Param2 from Env2" $ do
n <- runRIO env2Impl $ do
view (getL @Param2)
n `shouldBe` Param2 2
describe "runIF" $ do
it "runIF @Obj1 from Env1" $ do
n <- runRIO env1Impl $ do
runIF @Obj1 _method1
n `shouldBe` 1
it "runIF @Obj1 from Env2" $ do
n <- runRIO env2Impl $ do
runIF @Obj1 _method1
n `shouldBe` 1
it "runIF @Obj2 from Env2" $ do
n <- runRIO env2Impl $ do
runIF @Obj2 _method3
n `shouldBe` 3
it "runIF @Obj3 from Env1" $ do
n <- runRIO env1Impl $ do
_runObj3 obj3Impl
n `shouldBe` 4