packages feed

jordan-servant-0.1.0.0: lib/Jordan/Servant/Response.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}

-- | Helpers for rendering responses via Jordan.
module Jordan.Servant.Response where

import Data.Attoparsec.ByteString.Lazy (parseOnly)
import Data.Proxy
import GHC.Generics
import Jordan
import Servant.API
import Servant.API.ContentTypes
import Servant.API.Modifiers

-- | Wrapper to perform JSON serialization via Jordan.
--
-- Types used with this wrapper should have isomorphic 'Jordan.ToJSON' and 'Jordan.FromJSON' instances.
-- A utility is provided to check this.
newtype ViaJordan a = ViaJordan {getViaJordan :: a}
  deriving (Show, Read, Eq, Ord, Generic)

instance FromJSON a => FromJSON (ViaJordan a) where
  fromJSON = ViaJordan <$> fromJSON

instance ToJSON a => ToJSON (ViaJordan a) where
  toJSON = contramap getViaJordan toJSON

instance (HasStatus a) => HasStatus (ViaJordan a) where
  type StatusOf (ViaJordan a) = StatusOf a

-- | Overlapping instance: sidestep Aeson, use Jordan.
instance {-# OVERLAPPING #-} (ToJSON a) => MimeRender JSON (ViaJordan a) where
  mimeRender Proxy = toJSONViaBuilder . getViaJordan

-- | Overlapping instance: sidestep Aeson, just Jordan.
instance {-# OVERLAPPING #-} (FromJSON a) => MimeUnrender JSON (ViaJordan a) where
  mimeUnrender Proxy = parseOnly (ViaJordan <$> attoparsecParser)
  mimeUnrenderWithType Proxy _ = parseOnly (ViaJordan <$> attoparsecParser)