packages feed

lorentz-0.5.0: src/Lorentz/Annotation.hs

-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ

{-# LANGUAGE UndecidableSuperClasses #-}

-- | Type and field annotations for Lorentz types.
module Lorentz.Annotation
  ( HasAnnotation (..)
  , GHasAnnotation (..)
  , gGetAnnotationNoField
  , insertTypeAnn
  ) where

import qualified GHC.Generics as G
import Named (NamedF)

import Lorentz.Entrypoints.Helpers (ctorNameToAnn)
import Michelson.Text
import Michelson.Typed
  (BigMap, ContractRef(..), EpAddress, KnownIsoT, Notes(..), Operation, ToT, insertTypeAnn,
  starNotes)
import Michelson.Typed.Haskell.Value (GValueType)
import Michelson.Untyped (FieldAnn, TypeAnn, ann, noAnn)
import Tezos.Address
import Tezos.Core
import Tezos.Crypto
import Util.TypeLits

-- | Use this in the instance of @HasAnnotation@ when field annotations
-- should not be generated.
gGetAnnotationNoField
    :: forall a. (GHasAnnotation (G.Rep a), GValueType (G.Rep a) ~ ToT a) => Notes (ToT a)
gGetAnnotationNoField = fst $ gGetAnnotation @(G.Rep a) False

-- | This class defines the type and field annotations for a given type. Right now
-- the type annotations come from names in a named field, and field annotations are
-- generated from the record fields.
class HasAnnotation a where
  getAnnotation :: Notes (ToT a)
  default getAnnotation
    :: (GHasAnnotation (G.Rep a), GValueType (G.Rep a) ~ ToT a) => Notes (ToT a)
  getAnnotation = fst $ gGetAnnotation @(G.Rep a) True

instance (HasAnnotation a, KnownSymbol name)
  => HasAnnotation (NamedF Identity a name) where
  getAnnotation = insertTypeAnn (symbolAnn @name) $ getAnnotation @a
    where
      symbolAnn :: forall s. KnownSymbol s => TypeAnn
      symbolAnn = ann $ symbolValT' @s

instance (HasAnnotation (Maybe a), KnownSymbol name)
  => HasAnnotation (NamedF Maybe a name) where
  getAnnotation = getAnnotation @(NamedF Identity (Maybe a) name)

-- Primitive instances
instance (HasAnnotation a) => HasAnnotation (Maybe a) where
  getAnnotation = NTOption noAnn (getAnnotation @a)

instance HasAnnotation ()

instance HasAnnotation Integer where
  getAnnotation = starNotes

instance HasAnnotation Natural where
  getAnnotation = starNotes

instance HasAnnotation MText where
  getAnnotation = starNotes

instance HasAnnotation Bool where
  getAnnotation = starNotes

instance HasAnnotation ByteString where
  getAnnotation = starNotes

instance HasAnnotation Mutez where
  getAnnotation = starNotes

instance HasAnnotation Address where
  getAnnotation = starNotes

instance HasAnnotation EpAddress where
  getAnnotation = starNotes

instance HasAnnotation KeyHash where
  getAnnotation = starNotes

instance HasAnnotation Timestamp where
  getAnnotation = starNotes

instance HasAnnotation PublicKey where
  getAnnotation = starNotes

instance HasAnnotation Signature where
  getAnnotation = starNotes

instance (HasAnnotation a) => HasAnnotation (ContractRef a) where
  getAnnotation = NTContract noAnn (getAnnotation @a)

instance (HasAnnotation k, HasAnnotation v) => HasAnnotation (Map k v) where
  getAnnotation = NTMap noAnn (getAnnotation @k) (getAnnotation @v)

instance (HasAnnotation k, HasAnnotation v) => HasAnnotation (BigMap k v) where
  getAnnotation = NTBigMap noAnn (getAnnotation @k) (getAnnotation @v)

instance (KnownIsoT v) => HasAnnotation (Set v) where
  getAnnotation = starNotes

instance (HasAnnotation a) => HasAnnotation [a] where
  getAnnotation = NTList noAnn (getAnnotation @a)

instance HasAnnotation Operation where
  getAnnotation = starNotes

instance (HasAnnotation a, HasAnnotation b) => HasAnnotation (a, b)
instance (HasAnnotation a, HasAnnotation b, HasAnnotation c) => HasAnnotation (a, b, c)
instance (HasAnnotation a, HasAnnotation b, HasAnnotation c, HasAnnotation d) => HasAnnotation (a, b, c, d)
instance (HasAnnotation a, HasAnnotation b, HasAnnotation c, HasAnnotation d, HasAnnotation e)
  => HasAnnotation (a, b, c, d, e)
instance (HasAnnotation a, HasAnnotation b, HasAnnotation c, HasAnnotation d, HasAnnotation e, HasAnnotation f)
  => HasAnnotation (a, b, c, d, e, f)
instance
  ( HasAnnotation a, HasAnnotation b, HasAnnotation c, HasAnnotation d, HasAnnotation e
  , HasAnnotation f, HasAnnotation g)
  => HasAnnotation (a, b, c, d, e, f, g)

-- | A Generic @HasAnnotation@ implementation
class GHasAnnotation a where
  -- | @Bool@ acts as a flag to determine whether or not field annotations
  -- should be set.
  gGetAnnotation :: Bool -> (Notes (GValueType a), FieldAnn)

instance GHasAnnotation G.U1 where
  gGetAnnotation _ = (starNotes, noAnn)

instance (GHasAnnotation x)
  => GHasAnnotation (G.M1 G.S ('G.MetaSel 'Nothing b c d) x)
  where
  gGetAnnotation b = gGetAnnotation @x b

instance (GHasAnnotation x, KnownSymbol a)
  => GHasAnnotation (G.M1 G.S ('G.MetaSel ('Just a) b c d) x)
  where
  gGetAnnotation b = case b of
    True -> (fst $ gGetAnnotation @x b, ctorNameToAnn @a)
    False -> (fst $ gGetAnnotation @x b, noAnn)

instance (GHasAnnotation x) => GHasAnnotation (G.M1 G.C i1 x) where
  gGetAnnotation b = gGetAnnotation @x b

instance (GHasAnnotation x) => GHasAnnotation (G.M1 G.D i1 x) where
  gGetAnnotation b = gGetAnnotation @x b

instance
    ( GHasAnnotation x
    , GHasAnnotation y
    )
  =>
    GHasAnnotation (x G.:+: y)
  where
  gGetAnnotation b =
    ( NTOr noAnn noAnn noAnn
        (fst $ gGetAnnotation @x b)
        (fst $ gGetAnnotation @y b )
    , noAnn
    )

instance
    ( GHasAnnotation x
    , GHasAnnotation y
    )
  =>
    GHasAnnotation (x G.:*: y)
  where
  gGetAnnotation b =
    let  (xTypeAnn, xFieldAnn) = gGetAnnotation @x b
         (yTypeAnn, yFieldAnn) = gGetAnnotation @y b
    in
      ( NTPair noAnn
          xFieldAnn yFieldAnn
          xTypeAnn yTypeAnn
      , noAnn
      )

instance (HasAnnotation x) => GHasAnnotation (G.Rec0 x) where
  gGetAnnotation _ = (getAnnotation @x, noAnn)