packages feed

packstream-bolt-0.1.0.0: src/Data/PackStream.hs

-- | PackStream serialization: encode and decode Haskell values to\/from
-- the Neo4j PackStream binary format.
module Data.PackStream (
  -- * Simple interface to pack and unpack msgpack binary
  -- ** Lazy 'L.ByteString'
  pack, unpack,

  -- ** Strict 'L.ByteString'
  pack', unpack',

  lookupWithError, fromOneField,

  lookupMaybe, lookupMaybeError,

  -- * Re-export modules
  module Data.PackStream.Assoc,
  module Data.PackStream.Get,
  module Data.PackStream.Ps,
  module Data.PackStream.Put,
  module Data.PackStream.Result,
  ) where

import           Compat.Binary           (get, runGet, runGet', runPutLazy, runPut')
import qualified Data.ByteString         as S
import qualified Data.ByteString.Lazy    as L
import qualified Data.Text               as T

import           Data.PackStream.Assoc
import           Data.PackStream.Get
import           Data.PackStream.Ps
import           Data.PackStream.Put
import           Data.PackStream.Result
import Data.Vector as V
import Data.HashMap.Lazy as H

-- | Pack a Haskell value to PackStream binary.
pack :: PackStream a => a -> L.ByteString
pack = runPutLazy . toBinary

-- | Unpack PackStream binary to a Haskell value. If it fails, it returns 'Left' with an error message.
unpack :: PackStream a => L.ByteString -> Either T.Text a
unpack bs = do
  obj <- runGet bs get
  case fromPs obj of
    Success a -> Right a
    Error e   -> Left e


-- | Variant of 'pack' serializing to a strict 'ByteString'
pack' :: PackStream a => a -> S.ByteString
pack' = runPut' . toBinary

-- | Variant of 'unpack' serializing to a strict 'ByteString'
unpack' :: PackStream a => S.ByteString -> Either T.Text a
unpack' bs = do
  obj <- runGet' bs get
  case fromPs obj of
    Success a -> Right a
    Error e   -> Left e

-- | Look up a key in a dictionary, returning a custom error if absent.
lookupWithError :: PackStream v => T.Text -> H.HashMap T.Text Ps -> T.Text -> Result v
lookupWithError key map err =
  case H.lookup key map of
    Nothing -> Error err
    Just val -> fromPs val

-- | Decode a single-field structure vector, applying the constructor on success.
fromOneField :: PackStream a => V.Vector Ps -> (a -> b) -> T.Text -> Result b
fromOneField fields constructor error =
  if V.length fields /= 1 then
    Error error
  else do
    arg <- fromPs $ V.unsafeHead fields
    Success $ constructor arg

-- | Look up an optional key in a dictionary, returning 'Nothing' if absent.
lookupMaybe :: PackStream v => T.Text -> HashMap T.Text Ps -> Result (Maybe v)
lookupMaybe key map =
  case H.lookup key map of
    Nothing -> Success Nothing
    Just val -> fmap Just $ fromPs val

-- | Look up a required key in a dictionary, returning 'Left' with an error if absent.
lookupMaybeError :: PackStream v => T.Text -> HashMap T.Text Ps -> Either T.Text v
lookupMaybeError key map =
  case H.lookup key map of
    Nothing -> Left $ "\"" <> key <> "\" could not be found"
    Just val -> case fromPs val of
      Error err -> Left err
      Success v -> Right v