{-# LANGUAGE NamedFieldPuns  #-}
{-# LANGUAGE PatternSynonyms #-}

module Ouroboros.Network.CodecCBORTerm
  ( CodecCBORTerm (..)
  , VersionDataCodec
  , VersionedCodecCBORTerm (.., VersionDataCodec, encodeData, decodeData)
  , mkVersionedCodecCBORTerm
  , unVersionCodecCBORTerm
  ) where

import Codec.CBOR.Term qualified as CBOR
import Data.Text (Text)


-- | A pure codec which encodes to / decodes from 'CBOR.Term'.  This is useful
-- if one expects a valid @cbor@ encoding, which one might not know how to
-- decode like in the 'Handshake' protocol.
--
data CodecCBORTerm fail a = CodecCBORTerm
  { forall fail a. CodecCBORTerm fail a -> a -> Term
encodeTerm :: a -> CBOR.Term
  , forall fail a. CodecCBORTerm fail a -> Term -> Either fail a
decodeTerm :: CBOR.Term -> Either fail a
  }


-- | A pure codec which encodes to / decodes from `CBOR.Term` which can
-- depend on a version.
--
data VersionedCodecCBORTerm fail v a = VersionedCodecCBORTerm {
    forall fail v a. VersionedCodecCBORTerm fail v a -> v -> a -> Term
encodeVersionedTerm :: v -> a -> CBOR.Term,
    forall fail v a.
VersionedCodecCBORTerm fail v a -> v -> Term -> Either fail a
decodeVersionedTerm :: v -> CBOR.Term -> Either fail a
  }

mkVersionedCodecCBORTerm :: (vNumber -> CodecCBORTerm fail vData)
                         -> VersionedCodecCBORTerm  fail vNumber vData
mkVersionedCodecCBORTerm :: forall vNumber fail vData.
(vNumber -> CodecCBORTerm fail vData)
-> VersionedCodecCBORTerm fail vNumber vData
mkVersionedCodecCBORTerm vNumber -> CodecCBORTerm fail vData
codec = VersionedCodecCBORTerm {
      encodeVersionedTerm :: vNumber -> vData -> Term
encodeVersionedTerm = CodecCBORTerm fail vData -> vData -> Term
forall fail a. CodecCBORTerm fail a -> a -> Term
encodeTerm (CodecCBORTerm fail vData -> vData -> Term)
-> (vNumber -> CodecCBORTerm fail vData)
-> vNumber
-> vData
-> Term
forall b c a. (b -> c) -> (a -> b) -> a -> c
. vNumber -> CodecCBORTerm fail vData
codec,
      decodeVersionedTerm :: vNumber -> Term -> Either fail vData
decodeVersionedTerm = CodecCBORTerm fail vData -> Term -> Either fail vData
forall fail a. CodecCBORTerm fail a -> Term -> Either fail a
decodeTerm (CodecCBORTerm fail vData -> Term -> Either fail vData)
-> (vNumber -> CodecCBORTerm fail vData)
-> vNumber
-> Term
-> Either fail vData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. vNumber -> CodecCBORTerm fail vData
codec
    }

unVersionCodecCBORTerm :: VersionedCodecCBORTerm fail vNumber vData
                       -> vNumber -> CodecCBORTerm fail vData
unVersionCodecCBORTerm :: forall fail vNumber vData.
VersionedCodecCBORTerm fail vNumber vData
-> vNumber -> CodecCBORTerm fail vData
unVersionCodecCBORTerm VersionedCodecCBORTerm{vNumber -> vData -> Term
encodeVersionedTerm :: forall fail v a. VersionedCodecCBORTerm fail v a -> v -> a -> Term
encodeVersionedTerm :: vNumber -> vData -> Term
encodeVersionedTerm, vNumber -> Term -> Either fail vData
decodeVersionedTerm :: forall fail v a.
VersionedCodecCBORTerm fail v a -> v -> Term -> Either fail a
decodeVersionedTerm :: vNumber -> Term -> Either fail vData
decodeVersionedTerm} vNumber
v =
    CodecCBORTerm {
      encodeTerm :: vData -> Term
encodeTerm = vNumber -> vData -> Term
encodeVersionedTerm vNumber
v,
      decodeTerm :: Term -> Either fail vData
decodeTerm = vNumber -> Term -> Either fail vData
decodeVersionedTerm vNumber
v
    }

--
-- A specialised VersionedCodecCBORTerm used for encoding / decoding
-- handshake's version data
--

type VersionDataCodec versionNumber versionData =
    VersionedCodecCBORTerm Text versionNumber versionData

-- | Codec for version data exchanged by the handshake protocol.
--
pattern VersionDataCodec :: (v -> a -> CBOR.Term)
                         -> (v -> CBOR.Term -> Either Text a)
                         -> VersionDataCodec v a
pattern $mVersionDataCodec :: forall {r} {v} {a}.
VersionDataCodec v a
-> ((v -> a -> Term) -> (v -> Term -> Either Text a) -> r)
-> ((# #) -> r)
-> r
$bVersionDataCodec :: forall v a.
(v -> a -> Term)
-> (v -> Term -> Either Text a) -> VersionDataCodec v a
VersionDataCodec { forall v a. VersionDataCodec v a -> v -> a -> Term
encodeData, forall v a. VersionDataCodec v a -> v -> Term -> Either Text a
decodeData } =
    VersionedCodecCBORTerm encodeData decodeData
{-# COMPLETE VersionDataCodec #-}