packages feed

hs-multiaddr-0.1.0.0: src/Data/MultiaddrPart.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveDataTypeable #-}

module Data.MultiaddrPart
  (
    MultiaddrPart (..),
    toString,
    parse,
    encode,
    parseB,
    partHdrS,
    partHdrB
  ) where

import qualified Text.ParserCombinators.ReadP as Parser
import qualified Data.ByteString as BSStrict
import qualified Data.Multiaddr.IPFS as IPFS
import qualified Data.Multiaddr.VarInt as VarInt
import qualified Data.Multiaddr.IPv4 as IPv4
import qualified Data.Multiaddr.IPv6 as IPv6
import qualified Data.Multiaddr.Port as Port
import qualified Data.Multiaddr.Onion as Onion
import qualified Data.Multiaddr.UnixPath as UnixPath

import GHC.Generics (Generic)
import Data.Typeable (Typeable)
import Control.Applicative (some, (<|>))
import Control.Monad (unless)
import Data.Serialize.Get (Get)

data MultiaddrPart = IP4m   { ip4m   :: IPv4.IPv4 }
                   | IP6m   { ip6m   :: IPv6.IPv6 }
                   | TCPm   { tcpm   :: Port.Port }
                   | UDPm   { udpm   :: Port.Port }
                   | DCCPm  { dccpm  :: Port.Port }
                   | SCTPm  { sctpm  :: Port.Port }
                   | ONIONm { onionm :: Onion.Onion }
                   | IPFSm  { ipfsm :: IPFS.MultihashDigest }
                   | P2Pm   { p2pm  :: IPFS.MultihashDigest }
                   | UNIXm  { unixm  :: UnixPath.UnixPath }
                   | UTPm
                   | UDTm
                   | QUICm
                   | HTTPm
                   | HTTPSm
                   | WSm
                   | WSSm
                   deriving (Show, Eq, Generic, Typeable)

partHdrB :: MultiaddrPart -> VarInt.VarInt Int
partHdrB (IP4m _)   = 4
partHdrB (IP6m _)   = 41
partHdrB (TCPm _)   = 6
partHdrB (UDPm _)   = 17
partHdrB (DCCPm _)  = 33
partHdrB (SCTPm _)  = 132
partHdrB (ONIONm _) = 444
partHdrB (IPFSm _)  = 421
partHdrB (P2Pm _)   = 420
partHdrB (UNIXm _)  = 400
partHdrB UTPm       = 302
partHdrB UDTm       = 301
partHdrB QUICm      = 81
partHdrB HTTPm      = 480
partHdrB HTTPSm     = 443
partHdrB WSm        = 477
partHdrB WSSm       = 478

partHdrS :: MultiaddrPart -> String
partHdrS (IP4m _)   = "ip4"
partHdrS (IP6m _)   = "ip6"
partHdrS (TCPm _)   = "tcp"
partHdrS (UDPm _)   = "udp"
partHdrS (DCCPm _)  = "dccp"
partHdrS (SCTPm _)  = "sctp"
partHdrS (ONIONm _) = "onion"
partHdrS (IPFSm _)  = "ipfs"
partHdrS (P2Pm _)   = "p2p"
partHdrS (UNIXm _)  = "unix"
partHdrS UTPm       = "utp"
partHdrS UDTm       = "udt"
partHdrS QUICm      = "quic"
partHdrS HTTPm      = "http"
partHdrS HTTPSm     = "https"
partHdrS WSm        = "ws"
partHdrS WSSm       = "wss"

toString :: MultiaddrPart -> String
toString p = concat ["/", partHdrS p, go p]
  where
    go (IP4m i)   = "/" ++ IPv4.toString i
    go (IP6m i)   = "/" ++ IPv6.toString i
    go (TCPm p)   = "/" ++ Port.toString p
    go (UDPm p)   = "/" ++ Port.toString p
    go (DCCPm p)  = "/" ++ Port.toString p
    go (SCTPm p)  = "/" ++ Port.toString p
    go (ONIONm o) = "/" ++ Onion.toString o
    go (IPFSm h)  = "/" ++ IPFS.toString h
    go (P2Pm h)   = "/" ++ IPFS.toString h
    go (UNIXm p)  = "/" ++ UnixPath.toString p
    go UTPm       = ""
    go UDTm       = ""
    go QUICm      = ""
    go HTTPm      = ""
    go HTTPSm     = ""
    go WSm        = ""
    go WSSm       = ""

parse :: Parser.ReadP MultiaddrPart
parse =
  (IP4m <$> (parseHdr (IP4m undefined) *> parseSep *> IPv4.parse)) <|>
  (IP6m <$> (parseHdr (IP6m undefined) *> parseSep *> IPv6.parse)) <|>
  (TCPm <$> (parseHdr (TCPm undefined) *> parseSep *> Port.parse)) <|>
  (UDPm <$> (parseHdr (UDPm undefined) *> parseSep *> Port.parse)) <|>
  (DCCPm <$> (parseHdr (DCCPm undefined) *> parseSep *> Port.parse)) <|>
  (SCTPm <$> (parseHdr (SCTPm undefined) *> parseSep *> Port.parse)) <|>
  (ONIONm <$> (parseHdr (ONIONm undefined) *> parseSep *> Onion.parse)) <|>
  (IPFSm <$> (parseHdr (IPFSm undefined) *> parseSep *> IPFS.parse)) <|>
  (P2Pm <$> (parseHdr (P2Pm undefined) *> parseSep *> IPFS.parse)) <|>
  (UNIXm <$> (parseHdr (UNIXm undefined) *> parseSep *> UnixPath.parse)) <|>
  (parseHdr UTPm) <|>
  (parseHdr UDTm) <|>
  (parseHdr QUICm) <|>
  (parseHdr HTTPm) <|>
  (parseHdr HTTPSm) <|>
  (parseHdr WSm) <|>
  (parseHdr WSSm)
  where
    parseHdr c = c <$ (parseSep *> Parser.string (partHdrS c))
    parseSep = some $ Parser.char '/'

encode :: MultiaddrPart -> BSStrict.ByteString
encode p = BSStrict.append (VarInt.encode $ partHdrB p) (go p)
  where
    go (IP4m i)   = IPv4.encode i
    go (IP6m i)   = IPv6.encode i
    go (TCPm p)   = Port.encode p
    go (UDPm p)   = Port.encode p
    go (DCCPm p)  = Port.encode p
    go (SCTPm p)  = Port.encode p
    go (ONIONm o) = Onion.encode o
    go (IPFSm h)  = IPFS.encode h
    go (P2Pm h)   = IPFS.encode h
    go (UNIXm p)  = UnixPath.encode p
    go UTPm       = BSStrict.empty
    go UDTm       = BSStrict.empty
    go QUICm      = BSStrict.empty
    go HTTPm      = BSStrict.empty
    go HTTPSm     = BSStrict.empty
    go WSm        = BSStrict.empty
    go WSSm       = BSStrict.empty

parseB :: Get MultiaddrPart
parseB =
  (IP4m <$> (parseHdrB (IP4m undefined) *> IPv4.parseB)) <|>
  (IP6m <$> (parseHdrB (IP6m undefined) *> IPv6.parseB)) <|>
  (TCPm <$> (parseHdrB (TCPm undefined) *> Port.parseB)) <|>
  (UDPm <$> (parseHdrB (UDPm undefined) *> Port.parseB)) <|>
  (DCCPm <$> (parseHdrB (DCCPm undefined) *> Port.parseB)) <|>
  (SCTPm <$> (parseHdrB (SCTPm undefined) *> Port.parseB)) <|>
  (ONIONm <$> (parseHdrB (ONIONm undefined) *> Onion.parseB)) <|>
  (IPFSm <$> (parseHdrB (IPFSm undefined) *> IPFS.parseB)) <|>
  (P2Pm <$> (parseHdrB (P2Pm undefined) *> IPFS.parseB)) <|>
  (UNIXm <$> (parseHdrB (UNIXm undefined) *> UnixPath.parseB)) <|>
  (parseHdrB UTPm) <|>
  (parseHdrB UDTm) <|>
  (parseHdrB QUICm) <|>
  (parseHdrB HTTPm) <|>
  (parseHdrB HTTPSm) <|>
  (parseHdrB WSm) <|>
  (parseHdrB WSSm)
  where
    parseHdrB c = c <$ do
      i <- VarInt.decode
      unless (i == partHdrB c) $ fail "Wrong protocol index"