packages feed

morley-1.16.3: src/Morley/Micheline/Json.hs

-- SPDX-FileCopyrightText: 2018 obsidian.systems
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-obsidian-systems

-- | Module that defines helper types and functions that are related
-- to Micheline.
module Morley.Micheline.Json
  ( StringEncode (..)
  , TezosBigNum
  , TezosInt64
  , TezosMutez (..)
  , TezosNat
  ) where

import Data.Aeson (FromJSON, ToJSON, parseJSON, toEncoding, toJSON)
import Data.Aeson.Encoding qualified as AE
import Data.Aeson.Types qualified as Aeson
import Data.Bits (Bits)
import Data.Typeable (typeRep)
import Fmt (Buildable(..))
import Text.Show qualified as T

import Morley.Tezos.Core (Mutez(..), mkMutez)
import Unsafe qualified (unsafeM)

printAsString :: (PrettyShow a, Show a) => a -> Aeson.Value
printAsString a = Aeson.String $ show a

parseAsString :: forall a. (Read a, Typeable a) => Aeson.Value -> Aeson.Parser a
parseAsString = Aeson.withText (T.show $ typeRep (Proxy :: Proxy a)) $ \txt ->
  maybe (fail "Failed to parse string") pure $ readMaybe (toString txt)

parseStringEncodedIntegral :: (Read a, Typeable a) => Aeson.Value -> Aeson.Parser (StringEncode a)
parseStringEncodedIntegral x = StringEncode <$> parseAsString x

newtype StringEncode a = StringEncode { unStringEncode :: a }
  deriving stock (Generic, Eq, Ord, Bounded, Read, Show)
  deriving newtype (Enum, Num, Integral, Bits, Real, NFData, Hashable)

type instance IntBaseType (StringEncode a) = IntBaseType a

type TezosBigNum = StringEncode Integer

instance FromJSON TezosBigNum where
  parseJSON = parseStringEncodedIntegral

instance ToJSON TezosBigNum where
  toJSON (StringEncode x) = Aeson.String $ show x
  toEncoding (StringEncode x) = AE.integerText x

type TezosInt64 = StringEncode Int64

instance FromJSON TezosInt64 where
  parseJSON = parseStringEncodedIntegral

instance Buildable TezosInt64 where
  build = show . unStringEncode

instance ToJSON TezosInt64 where
  toJSON (StringEncode x) = Aeson.String $ show x
  toEncoding (StringEncode x) = AE.int64Text x

newtype TezosMutez = TezosMutez { unTezosMutez :: Mutez }
  deriving stock (Show, Eq, Ord)

instance ToJSON TezosMutez where
  toJSON = printAsString . unMutez . unTezosMutez

instance FromJSON TezosMutez where
  parseJSON v = do
    i     <- parseAsString @Int64 v
    mutez <- Unsafe.unsafeM $ mkMutez i
    pure $ TezosMutez mutez

type TezosNat = StringEncode Natural

instance Buildable TezosNat where
  build = show . unStringEncode

instance ToJSON TezosNat where
  toJSON (StringEncode x) = Aeson.String $ show x
  toEncoding (StringEncode x) = AE.integerText $ fromIntegral x

instance FromJSON TezosNat where
  parseJSON = parseStringEncodedIntegral