-- SPDX-FileCopyrightText: 2020 Tocqueville Group -- -- SPDX-License-Identifier: LicenseRef-MIT-TQ {-# OPTIONS_GHC -Wno-star-is-type #-} module Test.AsRPC ( unit_Renames_constructors_fields_and_generic_metadata , unit_Can_derive_many_instances_at_once , unit_Supports_higher_kinded_types , unit_Can_derive_instances_with_newtype , unit_Can_derive_many_instances_with_newtypes , unit_Can_derive_many_instances_with_type_aliases ) where import Data.Typeable ((:~:)(Refl)) import GHC.Generics import qualified Language.Haskell.TH.Syntax as TH import Test.Tasty.HUnit (Assertion) import Lorentz (BigMap, BigMapId, IsoValue(ToT), MText, customGeneric, leftBalanced, ligoLayout) import Morley.Client.RPC.AsRPC import Test.Util (shouldCompileIgnoringInstance, shouldCompileTo) data ExampleStorage a b = ExampleStorage { _esField1 :: Integer , _esField2 :: [BigMap Integer MText] , _esField3 :: a } deriving stock Generic deriving anyclass IsoValue deriveRPC "ExampleStorage" unit_Renames_constructors_fields_and_generic_metadata :: Assertion unit_Renames_constructors_fields_and_generic_metadata = do $(deriveRPC "ExampleStorage" >>= TH.lift) `shouldCompileTo` [d| data ExampleStorageRPC a (b :: k) = ExampleStorageRPC { _esField1RPC :: AsRPC Integer , _esField2RPC :: AsRPC [BigMap Integer MText] , _esField3RPC :: AsRPC a } type instance AsRPC (ExampleStorage a (b :: k)) = ExampleStorageRPC a (b :: k) deriving anyclass instance IsoValue (AsRPC a) => IsoValue (ExampleStorageRPC a (b :: k)) instance Generic (ExampleStorageRPC a (b :: k)) where type Rep (ExampleStorageRPC a (b :: k)) = D1 ('MetaData "ExampleStorageRPC" "Test.AsRPC" "main" 'False) (C1 ('MetaCons "ExampleStorageRPC" 'PrefixI 'True) ((:*:) (S1 ('MetaSel ('Just "_esField1RPC") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (AsRPC Integer))) ((:*:) (S1 ('MetaSel ('Just "_esField2RPC") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (AsRPC ([BigMap Integer MText])))) (S1 ('MetaSel ('Just "_esField3RPC") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (AsRPC a)))))) from (ExampleStorageRPC v0 v1 v2) = M1 (M1 ((:*:) (M1 (K1 v0)) ((:*:) (M1 (K1 v1)) (M1 (K1 v2))))) to (M1 (M1 ((:*:) (M1 (K1 v0)) ((:*:) (M1 (K1 v1)) (M1 (K1 v2)))))) = ExampleStorageRPC v0 v1 v2 |] data Ex1 = Ex1 Integer Ex1Inner deriving stock Generic deriving anyclass IsoValue data Ex1Inner = Ex1Inner Integer deriving stock Generic deriving anyclass IsoValue data Ex2 = Ex2 Integer deriving stock Generic deriving anyclass IsoValue deriveRPC "Ex2" data Ex3 = Ex3 Integer deriving stock (Generic, Eq, Ord) deriving anyclass IsoValue data Ex4 a = Ex4 a deriving stock Generic deriving anyclass IsoValue data ExampleMany = ExampleMany { _emField1 :: Integer , _emField2 :: Ex1 , _emField3 :: Ex2 , _emField4 :: [BigMap Ex3 (Ex4 MText)] } deriving stock Generic deriving anyclass IsoValue -- Check that the declarations generated by `deriveManyRPC` actually compile. deriveManyRPC "ExampleMany" [] unit_Can_derive_many_instances_at_once :: Assertion unit_Can_derive_many_instances_at_once = do shouldCompileIgnoringInstance ''Generic $(deriveManyRPC "ExampleMany" ["Ex3"] >>= TH.lift) [d| data ExampleManyRPC = ExampleManyRPC {_emField1RPC :: AsRPC Integer , _emField2RPC :: AsRPC Ex1 , _emField3RPC :: AsRPC Ex2 , _emField4RPC :: AsRPC [BigMap Ex3 (Ex4 MText)] } type instance AsRPC ExampleMany = ExampleManyRPC deriving anyclass instance IsoValue ExampleManyRPC -- An instance is generated for Ex1 data Ex1RPC = Ex1RPC (AsRPC Integer) (AsRPC Ex1Inner) type instance AsRPC Ex1 = Ex1RPC deriving anyclass instance IsoValue Ex1RPC -- An instance is generated for Ex1's fields' types data Ex1InnerRPC = Ex1InnerRPC (AsRPC Integer) type instance AsRPC Ex1Inner = Ex1InnerRPC deriving anyclass instance IsoValue Ex1InnerRPC -- No instance is generated for Ex2, because one already exists -- No instance is generated for Ex3, because we explicitly said we don't want one -- An instance is generated for BigMap's concrete type arguments data Ex4RPC a = Ex4RPC (AsRPC a) type instance AsRPC (Ex4 a) = Ex4RPC a deriving anyclass instance IsoValue (AsRPC a) => IsoValue (Ex4RPC a) |] ---------------------------------------------------------------------------- -- AsRPC type family ---------------------------------------------------------------------------- checkLaws :: ToT (AsRPC t) :~: AsRPC (ToT t) -> () checkLaws Refl = () ---------------------------------------------------------------------------- -- Examples data types: -- -- Simple data type ---------------------------------------------------------------------------- data Simple = Simple Integer Integer [Integer] deriving stock Generic deriving anyclass IsoValue deriveRPC "Simple" data ExpectedSimpleRPC = ExpectedSimpleRPC Integer Integer [Integer] deriving stock Generic deriving anyclass IsoValue _checkSimple :: ToT SimpleRPC :~: ToT ExpectedSimpleRPC _checkSimple = Refl _checkSimpleLaws :: () _checkSimpleLaws = checkLaws @Simple Refl ---------------------------------------------------------------------------- -- Simple record data type ---------------------------------------------------------------------------- data SimpleRecord = SimpleRecord { _simpleRecordField1 :: Integer , _simpleRecordField2 :: Integer , _simpleRecordField3 :: [Integer] } deriving stock Generic deriving anyclass IsoValue deriveRPC "SimpleRecord" data ExpectedSimpleRecordRPC = ExpectedSimpleRecordRPC { eSimpleRecordField1 :: Integer , eSimpleRecordField2 :: Integer , eSimpleRecordField3 :: [Integer] } deriving stock Generic deriving anyclass IsoValue _checkSimpleRecord :: ToT SimpleRecordRPC :~: ToT ExpectedSimpleRecordRPC _checkSimpleRecord = Refl _checkSimpleRecordLaws :: () _checkSimpleRecordLaws = checkLaws @SimpleRecord Refl ---------------------------------------------------------------------------- -- Data type with bigmap fields ---------------------------------------------------------------------------- data WithBigMap = WithBigMap { _wbmField1 :: Integer , _wbmField2 :: BigMap Integer Integer , _wbmField3 :: [BigMap Integer Integer] } deriving stock Generic deriving anyclass IsoValue deriveRPC "WithBigMap" data ExpectedWithBigMapRPC = ExpectedWithBigMapRPC { expectedWbmField1 :: Integer , expectedWbmField2 :: BigMapId Integer Integer , expectedWbmField3 :: [BigMapId Integer Integer] } deriving stock Generic deriving anyclass IsoValue _checkWithBigMap :: ToT WithBigMapRPC :~: ToT ExpectedWithBigMapRPC _checkWithBigMap = Refl _checkWithBigMapLaws :: () _checkWithBigMapLaws = checkLaws @WithBigMap Refl ---------------------------------------------------------------------------- -- Data type with custom generic strategy ---------------------------------------------------------------------------- data WithGenericStrategy = WithGenericStrategy_1 Integer Integer Integer | WithGenericStrategy_2 Integer Integer Integer | WithGenericStrategy_3 Integer Integer Integer | WithGenericStrategy_4 Integer Integer Integer deriving anyclass instance IsoValue WithGenericStrategy customGeneric "WithGenericStrategy" leftBalanced deriveRPCWithStrategy "WithGenericStrategy" leftBalanced _checkWithGenericStrategy :: ToT WithGenericStrategyRPC :~: ToT WithGenericStrategy _checkWithGenericStrategy = Refl _checkWithGenericStrategyLaws :: () _checkWithGenericStrategyLaws = checkLaws @WithGenericStrategy Refl ---------------------------------------------------------------------------- -- Data type with reordered fields ---------------------------------------------------------------------------- data WithReordered = WithRedordered { _wrField1 :: Integer , _wrField3 :: MText , _wrField2 :: [Integer] } deriving anyclass instance IsoValue WithReordered customGeneric "WithReordered" ligoLayout deriveRPCWithStrategy "WithReordered" ligoLayout _checkWithReordered :: ToT WithReorderedRPC :~: ToT WithReordered _checkWithReordered = Refl _checkWithReorderedLaws :: () _checkWithReorderedLaws = checkLaws @WithReordered Refl ---------------------------------------------------------------------------- -- Data type with type variables ---------------------------------------------------------------------------- data WithTypeVariables a = WithTypeVariables { _wtvField1 :: a } deriving stock Generic deriving anyclass IsoValue deriveRPC "WithTypeVariables" data ExpectedWithTypeVariablesRPC = ExpectedWithTypeVariablesRPC { expectedWtvField1 :: BigMapId Integer MText } deriving stock Generic deriving anyclass IsoValue _checkWithTypeVariables :: ToT (WithTypeVariablesRPC (BigMap Integer MText)) :~: ToT ExpectedWithTypeVariablesRPC _checkWithTypeVariables = Refl _checkWithTypeVariablesLaws :: () _checkWithTypeVariablesLaws = checkLaws @(WithTypeVariables (BigMap Integer MText)) Refl ---------------------------------------------------------------------------- -- Data type with nested data types and type variables ---------------------------------------------------------------------------- data WithNested a = WithNested { _wnField1 :: WithNested2 a , _wnField2 :: [WithNested2 a] , _wnField3 :: WithNested2 [a] } deriving stock Generic deriving anyclass instance IsoValue a => IsoValue (WithNested a) data WithNested2 a = WithNested2 { _wn2Field1 :: a , _wn2Field2 :: [a] } deriving stock Generic deriving anyclass IsoValue deriveManyRPC "WithNested" [] data ExpectedWithNestedRPC = ExpectedWithNestedRPC { expectedWnField1 :: ExpectedWithNested2RPC , expectedWnField2 :: [ExpectedWithNested2RPC] , expectedWnField3 :: ExpectedWithNested2RPC' } deriving stock Generic deriving anyclass IsoValue data ExpectedWithNested2RPC = ExpectedWithNested2RPC { expectedWn2Field1 :: BigMapId Integer MText , expectedWn2Field2 :: [BigMapId Integer MText] } deriving stock Generic deriving anyclass IsoValue data ExpectedWithNested2RPC' = ExpectedWithNested2RPC' { expectedWn2Field1' :: [BigMapId Integer MText] , expectedWn2Field2' :: [[BigMapId Integer MText]] } deriving stock Generic deriving anyclass IsoValue _checkWithNested2 :: ToT (WithNested2RPC (BigMap Integer MText)) :~: ToT ExpectedWithNested2RPC _checkWithNested2 = Refl _checkWithNested :: ToT (WithNestedRPC (BigMap Integer MText)) :~: ToT ExpectedWithNestedRPC _checkWithNested = Refl _checkWithNestedLaws :: () _checkWithNestedLaws = checkLaws @(WithNested (BigMap Integer MText)) Refl _checkWithNested2Laws :: () _checkWithNested2Laws = checkLaws @(WithNested2 (BigMap Integer MText)) Refl ---------------------------------------------------------------------------- -- Data type with phantom type variables ---------------------------------------------------------------------------- data WithPhantom a b c = WithPhantom { _wpField1 :: Integer , _wpField2 :: b } deriving stock Generic deriving anyclass IsoValue deriveRPC "WithPhantom" data ExpectedWithPhantomRPC = ExpectedWithPhantomRPC { expectedWpField1 :: Integer , expectedWpField2 :: MText } deriving stock Generic deriving anyclass IsoValue _checkWithPhantom :: ToT (WithPhantomRPC Integer MText (BigMap Integer Integer)) :~: ToT ExpectedWithPhantomRPC _checkWithPhantom = Refl _checkWithPhantomLaws :: () _checkWithPhantomLaws = checkLaws @(WithPhantom Integer MText (BigMap Integer Integer)) Refl ---------------------------------------------------------------------------- -- Data type with higher-kinded type variables ---------------------------------------------------------------------------- data WithHigherKind f = WithHigherKind { _whkField1 :: WithHigherKindNested f } deriving stock Generic deriving anyclass instance (IsoValue (f Integer MText)) => IsoValue (WithHigherKind f) data WithHigherKindNested f = WithHigherKindNested { _whknField1 :: f Integer MText } deriving stock Generic deriving anyclass instance (IsoValue (f Integer MText)) => IsoValue (WithHigherKindNested f) deriveManyRPC "WithHigherKind" [] unit_Supports_higher_kinded_types :: Assertion unit_Supports_higher_kinded_types = do shouldCompileIgnoringInstance ''Generic $(deriveManyRPC "WithHigherKind" [] >>= TH.lift) [d| data WithHigherKindRPC (f :: * -> * -> *) = WithHigherKindRPC { _whkField1RPC :: AsRPC (WithHigherKindNested f) } type instance AsRPC (WithHigherKind (f :: * -> * -> *)) = WithHigherKindRPC (f :: * -> * -> *) deriving anyclass instance IsoValue (AsRPC (WithHigherKindNested f)) => IsoValue (WithHigherKindRPC (f :: * -> * -> *)) data WithHigherKindNestedRPC (f :: * -> * -> *) = WithHigherKindNestedRPC { _whknField1RPC :: AsRPC (f Integer MText) } type instance AsRPC (WithHigherKindNested (f :: * -> * -> *)) = WithHigherKindNestedRPC (f :: * -> * -> *) deriving anyclass instance IsoValue (AsRPC (f Integer MText)) => IsoValue (WithHigherKindNestedRPC (f :: * -> * -> *)) |] data ExpectedWithHigherKindRPC = ExpectedWithHigherKindRPC { expectedWhkField1 :: ExpectedWithHigherKindNestedRPC } deriving stock Generic deriving anyclass IsoValue data ExpectedWithHigherKindNestedRPC = ExpectedWithHigherKindNestedRPC { expectedWhknField1 :: Map Integer MText } deriving stock Generic deriving anyclass IsoValue _checkWithHigherKindNested :: ToT (WithHigherKindNestedRPC Map) :~: ToT ExpectedWithHigherKindNestedRPC _checkWithHigherKindNested = Refl _checkWithHigherKind :: ToT (WithHigherKindRPC Map) :~: ToT ExpectedWithHigherKindRPC _checkWithHigherKind = Refl _checkWithHigherKindLaws :: () _checkWithHigherKindLaws = checkLaws @(WithHigherKind Map) Refl _checkWithHigherKindNestedLaws :: () _checkWithHigherKindNestedLaws = checkLaws @(WithHigherKindNested Map) Refl ---------------------------------------------------------------------------- -- Newtypes allowed with deriveRPC and in deriveManyRPC ---------------------------------------------------------------------------- data Data1 b = Data1 b deriving stock (Generic, Eq, Ord) deriving anyclass IsoValue deriveRPC "Data1" newtype Nt1 a b = Nt1 [Data1 a] deriving stock (Generic, Eq, Ord) deriving anyclass instance IsoValue a => IsoValue (Nt1 a b) deriveRPC "Nt1" unit_Can_derive_instances_with_newtype :: Assertion unit_Can_derive_instances_with_newtype = do shouldCompileIgnoringInstance ''Generic $(deriveRPC "Nt1" >>= TH.lift) [d| newtype Nt1RPC a (b :: k) = Nt1RPC (AsRPC ([Data1 a])) type instance AsRPC (Nt1 a (b :: k)) = Nt1RPC a (b :: k) deriving anyclass instance IsoValue (AsRPC ([Data1 a])) => IsoValue (Nt1RPC a (b :: k)) |] newtype Nt2 = Nt2 { _nt2field :: Integer } deriving stock Generic deriving anyclass IsoValue newtype Nt3 a = Nt3 a deriving stock Generic deriving anyclass IsoValue newtype Nt4 a b = Nt4 (BigMap Integer MText) deriving stock Generic deriving anyclass IsoValue newtype Nt5 a = Nt5 a deriving stock Generic deriving anyclass IsoValue newtype Nt6 a = Nt6 (Nt5 a) deriving stock Generic deriving anyclass instance IsoValue a => IsoValue (Nt6 a) data Data2 a = Data2 (Nt6 a) deriving stock Generic deriving anyclass instance IsoValue a => IsoValue (Data2 a) data ExampleWithNewtypes = ExampleWithNewtypes { _ewnField1 :: Nt3 Nt2 , _ewnField2 :: Nt4 MText Integer , _ewnField3 :: Data2 Integer } deriving stock Generic deriving anyclass IsoValue deriveManyRPC "ExampleWithNewtypes" [] unit_Can_derive_many_instances_with_newtypes :: Assertion unit_Can_derive_many_instances_with_newtypes = do shouldCompileIgnoringInstance ''Generic $(deriveManyRPC "ExampleWithNewtypes" [] >>= TH.lift) [d| data ExampleWithNewtypesRPC = ExampleWithNewtypesRPC {_ewnField1RPC :: (AsRPC (Nt3 Nt2)) , _ewnField2RPC :: (AsRPC (Nt4 MText Integer)) , _ewnField3RPC :: (AsRPC (Data2 Integer)) } type instance AsRPC ExampleWithNewtypes = ExampleWithNewtypesRPC deriving anyclass instance IsoValue ExampleWithNewtypesRPC newtype Nt3RPC a = Nt3RPC (AsRPC a) type instance AsRPC (Nt3 a) = Nt3RPC a deriving anyclass instance IsoValue (AsRPC a) => IsoValue (Nt3RPC a) newtype Nt2RPC = Nt2RPC {_nt2fieldRPC :: (AsRPC Integer)} type instance AsRPC Nt2 = Nt2RPC deriving anyclass instance IsoValue Nt2RPC newtype Nt4RPC (a :: k) (b :: k) = Nt4RPC (AsRPC (BigMap Integer MText)) type instance AsRPC (Nt4 (a :: k) (b :: k)) = Nt4RPC (a :: k) (b :: k) deriving anyclass instance IsoValue (Nt4RPC (a :: k) (b :: k)) data Data2RPC a = Data2RPC (AsRPC (Nt6 a)) type instance AsRPC (Data2 a) = Data2RPC a deriving anyclass instance IsoValue (AsRPC (Nt6 a)) => IsoValue (Data2RPC a) newtype Nt6RPC a = Nt6RPC (AsRPC (Nt5 a)) type instance AsRPC (Nt6 a) = Nt6RPC a deriving anyclass instance IsoValue (AsRPC (Nt5 a)) => IsoValue (Nt6RPC a) newtype Nt5RPC a = Nt5RPC (AsRPC a) type instance AsRPC (Nt5 a) = Nt5RPC a deriving anyclass instance IsoValue (AsRPC a) => IsoValue (Nt5RPC a) |] ---------------------------------------------------------------------------- -- Type aliases allowed with deriveManyRPC ---------------------------------------------------------------------------- type Ty1 = Integer type Ty2 k v = BigMap k v type Ty3 phantom = Ty1 data Dt1 = Dt1 Integer deriving stock (Generic, Eq, Ord) deriving anyclass IsoValue data Dt2 a = Dt2 a deriving stock Generic deriving anyclass IsoValue type Ty4 = Dt1 type Ty5 a = Dt2 a data Dt3 k v = Dt3 k v deriving stock (Generic, Eq, Ord) deriving anyclass IsoValue type Ty6 v = Dt3 Integer v data Dt4 v = Dt4 (Ty6 v) deriving stock (Generic, Eq, Ord) deriving anyclass instance IsoValue v => IsoValue (Dt4 v) data Dt5 a = Dt5 a deriving stock Generic deriving anyclass IsoValue type Ty7 (f :: Type -> Type) = (f Integer) data Dt6 = Dt6 Integer deriving stock (Generic, Eq, Ord) deriving anyclass IsoValue data Dt7 a b = Dt7 a b deriving stock Generic deriving anyclass IsoValue data Dt8 = Dt8 [BigMap Integer MText] deriving stock Generic deriving anyclass IsoValue type Ty8 (f :: Type -> Type -> Type) a = f a Dt6 type Ty9 (f :: Type -> Type -> Type) a = Ty8 f a type Ty10 (f :: Type -> Type -> Type) a = Ty9 f a data ExampleTypeAliasMany = ExampleTypeAliasMany { _etamField1 :: Ty1 , _etamField2 :: Ty2 Integer MText , _etamField3 :: Ty3 Integer , _etamField4 :: Ty4 , _etamField5 :: Ty5 Integer , _etamField6 :: Ty6 MText , _etamField7 :: Ty7 Dt5 , _etamField8 :: Ty10 Dt7 Dt8 , _etamField9 :: Dt4 Integer } deriving stock Generic deriving anyclass IsoValue -- Check that the declarations generated by `deriveManyRPC` actually compile. deriveManyRPC "ExampleTypeAliasMany" [] unit_Can_derive_many_instances_with_type_aliases :: Assertion unit_Can_derive_many_instances_with_type_aliases = do shouldCompileIgnoringInstance ''Generic $(deriveManyRPC "ExampleTypeAliasMany" [] >>= TH.lift) [d| data ExampleTypeAliasManyRPC = ExampleTypeAliasManyRPC { _etamField1RPC :: AsRPC Ty1 , _etamField2RPC :: AsRPC (Ty2 Integer MText) , _etamField3RPC :: AsRPC (Ty3 Integer) , _etamField4RPC :: AsRPC Ty4 , _etamField5RPC :: AsRPC (Ty5 Integer) , _etamField6RPC :: AsRPC (Ty6 MText) , _etamField7RPC :: AsRPC (Ty7 Dt5) , _etamField8RPC :: AsRPC (Ty10 Dt7 Dt8) , _etamField9RPC :: AsRPC (Dt4 Integer) } type instance AsRPC ExampleTypeAliasMany = ExampleTypeAliasManyRPC deriving anyclass instance IsoValue ExampleTypeAliasManyRPC -- An instance is generated for Dt1 data Dt1RPC = Dt1RPC (AsRPC Integer) type instance AsRPC Dt1 = Dt1RPC deriving anyclass instance IsoValue Dt1RPC -- An instance is generated for Dt2 data Dt2RPC a = Dt2RPC (AsRPC a) type instance AsRPC (Dt2 a) = Dt2RPC a deriving anyclass instance IsoValue (AsRPC a) => IsoValue (Dt2RPC a) -- An instance is generated for Dt3 data Dt3RPC k v = Dt3RPC (AsRPC k) (AsRPC v) type instance AsRPC (Dt3 k v) = Dt3RPC k v deriving anyclass instance (IsoValue (AsRPC k), IsoValue (AsRPC v)) => IsoValue (Dt3RPC k v) -- An instance is generated for Dt5 data Dt5RPC a = Dt5RPC (AsRPC a) type instance AsRPC (Dt5 a) = Dt5RPC a deriving anyclass instance IsoValue (AsRPC a) => IsoValue (Dt5RPC a) -- An instance is generated for Dt6 data Dt6RPC = Dt6RPC (AsRPC Integer) type instance AsRPC Dt6 = Dt6RPC deriving anyclass instance IsoValue Dt6RPC -- An instance is generated for Dt7 data Dt7RPC a b = Dt7RPC (AsRPC a) (AsRPC b) type instance AsRPC (Dt7 a b) = Dt7RPC a b deriving anyclass instance (IsoValue (AsRPC a), IsoValue (AsRPC b)) => IsoValue (Dt7RPC a b) -- An instance is generated for Dt8 data Dt8RPC = Dt8RPC (AsRPC ([BigMap Integer MText])) type instance AsRPC Dt8 = Dt8RPC deriving anyclass instance IsoValue Dt8RPC -- An instance is generated for Dt4 data Dt4RPC v = Dt4RPC (AsRPC (Ty6 v)) type instance AsRPC (Dt4 v) = Dt4RPC v deriving anyclass instance IsoValue (AsRPC (Ty6 v)) => IsoValue (Dt4RPC v) |]