packages feed

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

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

module Data.Multiaddr
  (
    -- * As Multiaddr types
    Multiaddr (..),
    P.MultiaddrPart (..),
    -- * As a string
    toString,
    toMultiaddr,
    -- * As binary packed format
    encode,
    decode,
    -- * Utilities
    P.partHdrS,
    P.partHdrB,
    encapsulate,
    decapsulate,
    findFirstPart,
    findLastPart,
    findAllParts
  ) where

import qualified Text.ParserCombinators.ReadP as Parser
import qualified Data.ByteString as BSStrict
import qualified Data.MultiaddrPart as P

import GHC.Generics (Generic)
import Data.Typeable (Typeable)
import Data.String (IsString, fromString)
import Control.Applicative (many, some)
import Data.Bytes.Get (runGetS)
import Data.Bytes.Put (runPutS)
import Data.Serialize.Get (Get)
import Data.List (isPrefixOf, find, filter)

newtype Multiaddr = Multiaddr { parts :: [P.MultiaddrPart] }
  deriving (Show, Eq, Monoid, Generic, Typeable)

instance IsString Multiaddr where
  fromString s = either error id $ toMultiaddr s

toString :: Multiaddr -> String
toString (Multiaddr p) = concatMap P.toString p

toMultiaddr :: String -> Either String Multiaddr
toMultiaddr s =
  case Parser.readP_to_S (parse <* Parser.eof) s of
    (m, ""):_ -> Right m
    otherwise -> Left $ "Data.Multiaddr.toMultiaddr: invalid multiaddr " ++ show s

parse :: Parser.ReadP Multiaddr
parse = do
  multiParts <- some P.parse
  many $ Parser.char '/'
  return $ Multiaddr multiParts

encode :: Multiaddr -> BSStrict.ByteString
encode (Multiaddr p) = BSStrict.concat . map P.encode $ p

decode :: BSStrict.ByteString -> Either String Multiaddr
decode = runGetS $ parseB

parseB :: Get Multiaddr
parseB = Multiaddr <$> some P.parseB

-- first address encapsulates the second address
encapsulate :: Multiaddr -> Multiaddr -> Multiaddr
encapsulate m1 m2 = Multiaddr $ parts m1 ++ parts m2

-- first address is prefix removed from the second address
decapsulate :: Multiaddr -> Multiaddr -> Maybe Multiaddr
decapsulate (Multiaddr p1) (Multiaddr p2)
  | isPrefixOf p1 p2 = Just . Multiaddr . drop (length p1) $ p2
  | otherwise = Nothing

findFirstPart :: P.MultiaddrPart -> Multiaddr -> Maybe P.MultiaddrPart
findFirstPart p a = find (\p' -> P.partHdrB p' == pI) (parts a)
  where
    pI = P.partHdrB p

findLastPart :: P.MultiaddrPart -> Multiaddr -> Maybe P.MultiaddrPart
findLastPart p a = go (\p' -> P.partHdrB p' == pI) (parts a) Nothing
  where
    pI = P.partHdrB p
    go _ [] lastPart = lastPart
    go pred (p:ps) lastPart
      | pred p    = go pred ps $ Just p
      | otherwise = go pred ps lastPart

findAllParts :: P.MultiaddrPart -> Multiaddr -> [P.MultiaddrPart]
findAllParts p a = filter (\p' -> P.partHdrB p' == pI) (parts a)
  where
    pI = P.partHdrB p