{-# LANGUAGE LambdaCase #-}

module Ouroboros.Network.PeerSelection.PeerSharing.Codec
  ( encodePortNumber
  , decodePortNumber
  , encodeRemoteAddress
  , decodeRemoteAddress
  ) where

import Codec.CBOR.Decoding qualified as CBOR
import Codec.CBOR.Encoding qualified as CBOR

import Network.Socket (PortNumber, SockAddr (..))
import Ouroboros.Network.NodeToNode.Version (NodeToNodeVersion (..))

encodePortNumber :: PortNumber -> CBOR.Encoding
encodePortNumber :: PortNumber -> Encoding
encodePortNumber = Word16 -> Encoding
CBOR.encodeWord16 (Word16 -> Encoding)
-> (PortNumber -> Word16) -> PortNumber -> Encoding
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PortNumber -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral

decodePortNumber :: CBOR.Decoder s PortNumber
decodePortNumber :: forall s. Decoder s PortNumber
decodePortNumber = Word16 -> PortNumber
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word16 -> PortNumber) -> Decoder s Word16 -> Decoder s PortNumber
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Decoder s Word16
forall s. Decoder s Word16
CBOR.decodeWord16


-- | This encoder should be faithful to the PeerSharing
-- CDDL Specification.
--
-- See the network design document for more details
---
-- /Invariant:/ not a unix socket address type.
---
encodeRemoteAddress :: NodeToNodeVersion -> SockAddr -> CBOR.Encoding
encodeRemoteAddress :: NodeToNodeVersion -> SockAddr -> Encoding
encodeRemoteAddress =
  \case
    NodeToNodeVersion
NodeToNodeV_13 -> SockAddr -> Encoding
sockAddr
    NodeToNodeVersion
NodeToNodeV_14 -> SockAddr -> Encoding
sockAddr

  where
    sockAddr :: SockAddr -> Encoding
sockAddr = \case
      SockAddrInet PortNumber
pn HostAddress
w -> Word -> Encoding
CBOR.encodeListLen Word
3
                        Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> Word -> Encoding
CBOR.encodeWord Word
0
                        Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> HostAddress -> Encoding
CBOR.encodeWord32 HostAddress
w
                        Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> PortNumber -> Encoding
encodePortNumber PortNumber
pn
      SockAddrInet6 PortNumber
pn HostAddress
_ (HostAddress
w1, HostAddress
w2, HostAddress
w3, HostAddress
w4) HostAddress
_ -> Word -> Encoding
CBOR.encodeListLen Word
6
                                            Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> Word -> Encoding
CBOR.encodeWord Word
1
                                            Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> HostAddress -> Encoding
CBOR.encodeWord32 HostAddress
w1
                                            Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> HostAddress -> Encoding
CBOR.encodeWord32 HostAddress
w2
                                            Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> HostAddress -> Encoding
CBOR.encodeWord32 HostAddress
w3
                                            Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> HostAddress -> Encoding
CBOR.encodeWord32 HostAddress
w4
                                            Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> PortNumber -> Encoding
encodePortNumber PortNumber
pn
      SockAddrUnix String
_ -> String -> Encoding
forall a. HasCallStack => String -> a
error String
"Should never be encoding a SockAddrUnix!"

-- | This decoder should be faithful to the PeerSharing
-- CDDL Specification.
--
-- See the network design document for more details
--
decodeRemoteAddress :: NodeToNodeVersion -> CBOR.Decoder s SockAddr
decodeRemoteAddress :: forall s. NodeToNodeVersion -> Decoder s SockAddr
decodeRemoteAddress =
  \case
    NodeToNodeVersion
NodeToNodeV_13 -> Decoder s SockAddr
forall {s}. Decoder s SockAddr
decoder13
    NodeToNodeVersion
NodeToNodeV_14 -> Decoder s SockAddr
forall {s}. Decoder s SockAddr
decoder13

  where
    decoder13 :: Decoder s SockAddr
decoder13 = do
      _ <- Decoder s Int
forall s. Decoder s Int
CBOR.decodeListLen
      tok <- CBOR.decodeWord
      case tok of
        Word
0 -> do
          w <- Decoder s HostAddress
forall s. Decoder s HostAddress
CBOR.decodeWord32
          pn <- decodePortNumber
          return (SockAddrInet pn w)
        Word
1 -> do
          w1 <- Decoder s HostAddress
forall s. Decoder s HostAddress
CBOR.decodeWord32
          w2 <- CBOR.decodeWord32
          w3 <- CBOR.decodeWord32
          w4 <- CBOR.decodeWord32
          pn <- decodePortNumber
          return (SockAddrInet6 pn 0 (w1, w2, w3, w4) 0)
        Word
_ -> String -> Decoder s SockAddr
forall a. String -> Decoder s a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Serialise.decode.SockAddr unexpected tok " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Word -> String
forall a. Show a => a -> String
show Word
tok)