{-# LANGUAGE DataKinds            #-}
{-# LANGUAGE KindSignatures       #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE TypeApplications     #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-specialise #-}
{-# OPTIONS_GHC -fno-omit-interface-pragmas #-}
module PlutusTx.IsData.Class where

import Prelude qualified as Haskell (Int, error)

import PlutusCore.Data qualified as PLC
import PlutusTx.Base
import PlutusTx.Builtins as Builtins
import PlutusTx.Builtins.Internal (BuiltinData (..))
import PlutusTx.Builtins.Internal qualified as BI
import PlutusTx.Maybe (Maybe (..))

import PlutusTx.Applicative
import PlutusTx.ErrorCodes
import PlutusTx.Trace

import Data.Kind
import Data.Void

import GHC.TypeLits (ErrorMessage (..), TypeError)


{- HLINT ignore -}

-- | A typeclass for types that can be converted to and from 'BuiltinData'.
class ToData (a :: Type) where
    -- | Convert a value to 'BuiltinData'.
    toBuiltinData :: a -> BuiltinData

class FromData (a :: Type) where
    -- TODO: this should probably provide some kind of diagnostics
    -- | Convert a value from 'BuiltinData', returning 'Nothing' if this fails.
    fromBuiltinData :: BuiltinData -> Maybe a

class UnsafeFromData (a :: Type) where
    -- | Convert a value from 'BuiltinData', calling 'error' if this fails.
    -- This is typically much faster than 'fromBuiltinData'.
    --
    -- When implementing this function, make sure to call 'unsafeFromBuiltinData'
    -- rather than 'fromBuiltinData' when converting substructures!
    unsafeFromBuiltinData :: BuiltinData -> a

instance ToData BuiltinData where
    {-# INLINABLE toBuiltinData #-}
    toBuiltinData :: BuiltinData -> BuiltinData
toBuiltinData = BuiltinData -> BuiltinData
forall a. a -> a
id
instance FromData BuiltinData where
    {-# INLINABLE fromBuiltinData #-}
    fromBuiltinData :: BuiltinData -> Maybe BuiltinData
fromBuiltinData BuiltinData
d = BuiltinData -> Maybe BuiltinData
forall a. a -> Maybe a
Just BuiltinData
d
instance UnsafeFromData BuiltinData where
    {-# INLINABLE unsafeFromBuiltinData #-}
    unsafeFromBuiltinData :: BuiltinData -> BuiltinData
unsafeFromBuiltinData BuiltinData
d = BuiltinData
d

instance (TypeError ('Text "Int is not supported, use Integer instead"))
    => ToData Haskell.Int where
    toBuiltinData :: Int -> BuiltinData
toBuiltinData = [Char] -> Int -> BuiltinData
forall a. HasCallStack => [Char] -> a
Haskell.error [Char]
"unsupported"
instance (TypeError ('Text "Int is not supported, use Integer instead"))
    => FromData Haskell.Int where
    fromBuiltinData :: BuiltinData -> Maybe Int
fromBuiltinData = [Char] -> BuiltinData -> Maybe Int
forall a. HasCallStack => [Char] -> a
Haskell.error [Char]
"unsupported"
instance (TypeError ('Text "Int is not supported, use Integer instead"))
    => UnsafeFromData Haskell.Int where
    unsafeFromBuiltinData :: BuiltinData -> Int
unsafeFromBuiltinData = [Char] -> BuiltinData -> Int
forall a. HasCallStack => [Char] -> a
Haskell.error [Char]
"unsupported"

instance ToData Integer where
    {-# INLINABLE toBuiltinData #-}
    toBuiltinData :: Integer -> BuiltinData
toBuiltinData Integer
i = Integer -> BuiltinData
mkI Integer
i
instance FromData Integer where
    {-# INLINABLE fromBuiltinData #-}
    fromBuiltinData :: BuiltinData -> Maybe Integer
fromBuiltinData BuiltinData
d = BuiltinData
-> (Integer -> BuiltinList BuiltinData -> Maybe Integer)
-> (BuiltinList (BuiltinPair BuiltinData BuiltinData)
    -> Maybe Integer)
-> (BuiltinList BuiltinData -> Maybe Integer)
-> (Integer -> Maybe Integer)
-> (BuiltinByteString -> Maybe Integer)
-> Maybe Integer
forall r.
BuiltinData
-> (Integer -> BuiltinList BuiltinData -> r)
-> (BuiltinList (BuiltinPair BuiltinData BuiltinData) -> r)
-> (BuiltinList BuiltinData -> r)
-> (Integer -> r)
-> (BuiltinByteString -> r)
-> r
matchData' BuiltinData
d (\Integer
_ BuiltinList BuiltinData
_ -> Maybe Integer
forall a. Maybe a
Nothing) (Maybe Integer
-> BuiltinList (BuiltinPair BuiltinData BuiltinData)
-> Maybe Integer
forall a b. a -> b -> a
const Maybe Integer
forall a. Maybe a
Nothing) (Maybe Integer -> BuiltinList BuiltinData -> Maybe Integer
forall a b. a -> b -> a
const Maybe Integer
forall a. Maybe a
Nothing) (\Integer
i -> Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
i) (Maybe Integer -> BuiltinByteString -> Maybe Integer
forall a b. a -> b -> a
const Maybe Integer
forall a. Maybe a
Nothing)
instance UnsafeFromData Integer where
    {-# INLINABLE unsafeFromBuiltinData #-}
    unsafeFromBuiltinData :: BuiltinData -> Integer
unsafeFromBuiltinData = BuiltinData -> Integer
BI.unsafeDataAsI

instance ToData Builtins.BuiltinByteString where
    {-# INLINABLE toBuiltinData #-}
    toBuiltinData :: BuiltinByteString -> BuiltinData
toBuiltinData = BuiltinByteString -> BuiltinData
mkB
instance FromData Builtins.BuiltinByteString where
    {-# INLINABLE fromBuiltinData #-}
    fromBuiltinData :: BuiltinData -> Maybe BuiltinByteString
fromBuiltinData BuiltinData
d = BuiltinData
-> (Integer -> BuiltinList BuiltinData -> Maybe BuiltinByteString)
-> (BuiltinList (BuiltinPair BuiltinData BuiltinData)
    -> Maybe BuiltinByteString)
-> (BuiltinList BuiltinData -> Maybe BuiltinByteString)
-> (Integer -> Maybe BuiltinByteString)
-> (BuiltinByteString -> Maybe BuiltinByteString)
-> Maybe BuiltinByteString
forall r.
BuiltinData
-> (Integer -> BuiltinList BuiltinData -> r)
-> (BuiltinList (BuiltinPair BuiltinData BuiltinData) -> r)
-> (BuiltinList BuiltinData -> r)
-> (Integer -> r)
-> (BuiltinByteString -> r)
-> r
matchData' BuiltinData
d (\Integer
_ BuiltinList BuiltinData
_ -> Maybe BuiltinByteString
forall a. Maybe a
Nothing) (Maybe BuiltinByteString
-> BuiltinList (BuiltinPair BuiltinData BuiltinData)
-> Maybe BuiltinByteString
forall a b. a -> b -> a
const Maybe BuiltinByteString
forall a. Maybe a
Nothing) (Maybe BuiltinByteString
-> BuiltinList BuiltinData -> Maybe BuiltinByteString
forall a b. a -> b -> a
const Maybe BuiltinByteString
forall a. Maybe a
Nothing) (Maybe BuiltinByteString -> Integer -> Maybe BuiltinByteString
forall a b. a -> b -> a
const Maybe BuiltinByteString
forall a. Maybe a
Nothing) (\BuiltinByteString
b -> BuiltinByteString -> Maybe BuiltinByteString
forall a. a -> Maybe a
Just BuiltinByteString
b)
instance UnsafeFromData Builtins.BuiltinByteString where
    {-# INLINABLE unsafeFromBuiltinData #-}
    unsafeFromBuiltinData :: BuiltinData -> BuiltinByteString
unsafeFromBuiltinData = BuiltinData -> BuiltinByteString
BI.unsafeDataAsB

instance ToData a => ToData [a] where
    {-# INLINABLE toBuiltinData #-}
    toBuiltinData :: [a] -> BuiltinData
toBuiltinData [a]
l = BuiltinList BuiltinData -> BuiltinData
BI.mkList ([a] -> BuiltinList BuiltinData
mapToBuiltin [a]
l)
        where
          {-# INLINE mapToBuiltin #-}
          mapToBuiltin :: [a] -> BI.BuiltinList BI.BuiltinData
          mapToBuiltin :: [a] -> BuiltinList BuiltinData
mapToBuiltin = [a] -> BuiltinList BuiltinData
go
            where
                go :: [a] -> BI.BuiltinList BI.BuiltinData
                go :: [a] -> BuiltinList BuiltinData
go []     = BuiltinUnit -> BuiltinList BuiltinData
BI.mkNilData BuiltinUnit
BI.unitval
                go (a
x:[a]
xs) = BuiltinData -> BuiltinList BuiltinData -> BuiltinList BuiltinData
forall a. a -> BuiltinList a -> BuiltinList a
BI.mkCons (a -> BuiltinData
forall a. ToData a => a -> BuiltinData
toBuiltinData a
x) ([a] -> BuiltinList BuiltinData
go [a]
xs)
instance FromData a => FromData [a] where
    {-# INLINABLE fromBuiltinData #-}
    fromBuiltinData :: BuiltinData -> Maybe [a]
fromBuiltinData BuiltinData
d =
        BuiltinData
-> (Integer -> BuiltinList BuiltinData -> Maybe [a])
-> (BuiltinList (BuiltinPair BuiltinData BuiltinData) -> Maybe [a])
-> (BuiltinList BuiltinData -> Maybe [a])
-> (Integer -> Maybe [a])
-> (BuiltinByteString -> Maybe [a])
-> Maybe [a]
forall r.
BuiltinData
-> (Integer -> BuiltinList BuiltinData -> r)
-> (BuiltinList (BuiltinPair BuiltinData BuiltinData) -> r)
-> (BuiltinList BuiltinData -> r)
-> (Integer -> r)
-> (BuiltinByteString -> r)
-> r
matchData'
        BuiltinData
d
        (\Integer
_ BuiltinList BuiltinData
_ -> Maybe [a]
forall a. Maybe a
Nothing)
        (Maybe [a]
-> BuiltinList (BuiltinPair BuiltinData BuiltinData) -> Maybe [a]
forall a b. a -> b -> a
const Maybe [a]
forall a. Maybe a
Nothing)
        BuiltinList BuiltinData -> Maybe [a]
traverseFromBuiltin
        (Maybe [a] -> Integer -> Maybe [a]
forall a b. a -> b -> a
const Maybe [a]
forall a. Maybe a
Nothing)
        (Maybe [a] -> BuiltinByteString -> Maybe [a]
forall a b. a -> b -> a
const Maybe [a]
forall a. Maybe a
Nothing)
        where
          {-# INLINE traverseFromBuiltin #-}
          traverseFromBuiltin :: BI.BuiltinList BI.BuiltinData -> Maybe [a]
          traverseFromBuiltin :: BuiltinList BuiltinData -> Maybe [a]
traverseFromBuiltin = BuiltinList BuiltinData -> Maybe [a]
go
            where
                go :: BI.BuiltinList BI.BuiltinData -> Maybe [a]
                go :: BuiltinList BuiltinData -> Maybe [a]
go BuiltinList BuiltinData
l = BuiltinList BuiltinData
-> (() -> Maybe [a]) -> (() -> Maybe [a]) -> () -> Maybe [a]
forall a b. BuiltinList a -> b -> b -> b
BI.chooseList BuiltinList BuiltinData
l (Maybe [a] -> () -> Maybe [a]
forall a b. a -> b -> a
const ([a] -> Maybe [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [])) (\()
_ -> (a -> [a] -> [a]) -> Maybe a -> Maybe [a] -> Maybe [a]
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (:) (BuiltinData -> Maybe a
forall a. FromData a => BuiltinData -> Maybe a
fromBuiltinData (BuiltinList BuiltinData -> BuiltinData
forall a. BuiltinList a -> a
BI.head BuiltinList BuiltinData
l)) (BuiltinList BuiltinData -> Maybe [a]
go (BuiltinList BuiltinData -> BuiltinList BuiltinData
forall a. BuiltinList a -> BuiltinList a
BI.tail BuiltinList BuiltinData
l))) ()
instance UnsafeFromData a => UnsafeFromData [a] where
    {-# INLINABLE unsafeFromBuiltinData #-}
    unsafeFromBuiltinData :: BuiltinData -> [a]
unsafeFromBuiltinData BuiltinData
d = BuiltinList BuiltinData -> [a]
mapFromBuiltin (BuiltinData -> BuiltinList BuiltinData
BI.unsafeDataAsList BuiltinData
d)
        where
          {-# INLINE mapFromBuiltin #-}
          mapFromBuiltin :: BI.BuiltinList BI.BuiltinData -> [a]
          mapFromBuiltin :: BuiltinList BuiltinData -> [a]
mapFromBuiltin = BuiltinList BuiltinData -> [a]
go
            where
                go :: BI.BuiltinList BI.BuiltinData -> [a]
                go :: BuiltinList BuiltinData -> [a]
go BuiltinList BuiltinData
l = BuiltinList BuiltinData -> (() -> [a]) -> (() -> [a]) -> () -> [a]
forall a b. BuiltinList a -> b -> b -> b
BI.chooseList BuiltinList BuiltinData
l ([a] -> () -> [a]
forall a b. a -> b -> a
const []) (\()
_ -> BuiltinData -> a
forall a. UnsafeFromData a => BuiltinData -> a
unsafeFromBuiltinData (BuiltinList BuiltinData -> BuiltinData
forall a. BuiltinList a -> a
BI.head BuiltinList BuiltinData
l) a -> [a] -> [a]
forall a. a -> [a] -> [a]
: BuiltinList BuiltinData -> [a]
go (BuiltinList BuiltinData -> BuiltinList BuiltinData
forall a. BuiltinList a -> BuiltinList a
BI.tail BuiltinList BuiltinData
l)) ()

instance ToData Void where
    {-# INLINABLE toBuiltinData #-}
    toBuiltinData :: Void -> BuiltinData
toBuiltinData Void
v = Void -> BuiltinData
forall a. Void -> a
absurd Void
v
instance FromData Void where
    {-# INLINABLE fromBuiltinData #-}
    fromBuiltinData :: BuiltinData -> Maybe Void
fromBuiltinData BuiltinData
_ = Maybe Void
forall a. Maybe a
Nothing
instance UnsafeFromData Void where
    {-# INLINABLE unsafeFromBuiltinData #-}
    unsafeFromBuiltinData :: BuiltinData -> Void
unsafeFromBuiltinData BuiltinData
_ = BuiltinString -> Void
forall a. BuiltinString -> a
traceError BuiltinString
voidIsNotSupportedError

-- | Convert a value to 'PLC.Data'.
toData :: (ToData a) => a -> PLC.Data
toData :: a -> Data
toData a
a = BuiltinData -> Data
builtinDataToData (a -> BuiltinData
forall a. ToData a => a -> BuiltinData
toBuiltinData a
a)

-- | Convert a value from 'PLC.Data', returning 'Nothing' if this fails.
fromData :: (FromData a) => PLC.Data -> Maybe a
fromData :: Data -> Maybe a
fromData Data
d = BuiltinData -> Maybe a
forall a. FromData a => BuiltinData -> Maybe a
fromBuiltinData (Data -> BuiltinData
BuiltinData Data
d)