{-# LANGUAGE DeriveDataTypeable, DerivingStrategies #-} -- | Untyped Michelson values (i. e. type of a value is not statically known). module Michelson.Untyped.Value ( Value (..) , Elt (..) -- Internal types to avoid orphan instances , InternalByteString(..) , unInternalByteString ) where import Data.Aeson (FromJSON(..), ToJSON(..)) import Data.Aeson.TH (defaultOptions, deriveJSON) import Data.Data (Data(..)) import Data.Text.Lazy.Builder (Builder) import Fmt (hexF, (+|), (|+)) import Formatting.Buildable (Buildable) import qualified Formatting.Buildable as Buildable data Value op = ValueInt Integer | ValueString Text | ValueBytes InternalByteString | ValueUnit | ValueTrue | ValueFalse | ValuePair (Value op) (Value op) | ValueLeft (Value op) | ValueRight (Value op) | ValueSome (Value op) | ValueNone | ValueNil | ValueSeq (NonEmpty $ Value op) -- ^ A sequence of elements: can be a list or a set. -- We can't distinguish lists and sets during parsing. | ValueMap (NonEmpty $ Elt op) | ValueLambda (NonEmpty op) deriving stock (Eq, Show, Functor, Data, Generic) data Elt op = Elt (Value op) (Value op) deriving stock (Eq, Show, Functor, Data, Generic) -- | ByteString does not have an instance for ToJSON and FromJSON, to -- avoid orphan type class instances, make a new type wrapper around it. newtype InternalByteString = InternalByteString ByteString deriving stock (Data, Eq, Show) unInternalByteString :: InternalByteString -> ByteString unInternalByteString (InternalByteString bs) = bs instance Buildable op => Buildable (Value op) where build = \case ValueInt i -> Buildable.build i ValueString s -> "\"" +| s |+ "\"" ValueBytes (InternalByteString b) -> "0x" <> hexF b ValueUnit -> "Unit" ValueTrue -> "True" ValueFalse -> "False" ValuePair a b -> "(Pair " +| a |+ " " +| b |+ ")" ValueLeft v -> "(Left " +| v |+ ")" ValueRight v -> "(Right " +| v |+ ")" ValueSome v -> "(Some " +| v |+ ")" ValueNone -> "None" ValueNil -> "{}" ValueSeq vs -> buildList vs ValueMap els -> buildList els ValueLambda ops -> buildList ops where buildList :: Buildable a => NonEmpty a -> Builder buildList (toList -> items) = "{" <> mconcat (intersperse "; " $ map Buildable.build items) <> "}" instance Buildable op => Buildable (Elt op) where build (Elt a b) = "Elt " +| a |+ " " +| b |+ "" ---------------------------------------------------------------------------- -- JSON serialization ---------------------------------------------------------------------------- -- it is not possible to derives these automatically because -- ByteString does not have a ToJSON or FromJSON instance instance ToJSON InternalByteString where toJSON = toJSON @Text . decodeUtf8 . unInternalByteString instance FromJSON InternalByteString where parseJSON = fmap (InternalByteString . encodeUtf8 @Text) . parseJSON deriveJSON defaultOptions ''Value deriveJSON defaultOptions ''Elt