module Web.Minion.Media.Json where

import Data.Aeson (FromJSON, ToJSON, eitherDecode)
import Data.Aeson qualified as Aeson
import Data.Bifunctor (Bifunctor (..))
import Data.ByteString.Builder qualified as Bytes.Builder
import Data.List.NonEmpty qualified as Nel
import Data.Text qualified as Text
import Network.HTTP.Media qualified as Http
import Network.HTTP.Types qualified as Http
import Network.Wai qualified as Wai
import Web.Minion.Media
import Web.Minion.Request.Body
import Web.Minion.Response.Body (Encode (encode))

data Json

instance ContentType Json where
  media :: NonEmpty MediaType
media =
    ByteString
"application" ByteString -> ByteString -> MediaType
Http.// ByteString
"json" MediaType -> (ByteString, ByteString) -> MediaType
Http./: (ByteString
"charset", ByteString
"utf-8")
      MediaType -> [MediaType] -> NonEmpty MediaType
forall a. a -> [a] -> NonEmpty a
Nel.:| [ByteString
"application" ByteString -> ByteString -> MediaType
Http.// ByteString
"json"]

instance (FromJSON a) => Decode Json a where
  decode :: ByteString -> Either Text a
decode = (String -> Text) -> Either String a -> Either Text a
forall a b c. (a -> b) -> Either a c -> Either b c
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first String -> Text
Text.pack (Either String a -> Either Text a)
-> (ByteString -> Either String a) -> ByteString -> Either Text a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String a
forall a. FromJSON a => ByteString -> Either String a
eitherDecode

instance (ToJSON a) => Encode Json a where
  encode :: a -> Response
encode a
a =
    Status -> ResponseHeaders -> Builder -> Response
Wai.responseBuilder
      Status
Http.status200
      [(HeaderName
Http.hContentType, MediaType -> ByteString
forall h. RenderHeader h => h -> ByteString
Http.renderHeader (MediaType -> ByteString) -> MediaType -> ByteString
forall a b. (a -> b) -> a -> b
$ NonEmpty MediaType -> MediaType
forall a. NonEmpty a -> a
Nel.head (NonEmpty MediaType -> MediaType)
-> NonEmpty MediaType -> MediaType
forall a b. (a -> b) -> a -> b
$ forall a. ContentType a => NonEmpty MediaType
forall {k} (a :: k). ContentType a => NonEmpty MediaType
media @Json)]
      (ByteString -> Builder
Bytes.Builder.lazyByteString (ByteString -> Builder) -> ByteString -> Builder
forall a b. (a -> b) -> a -> b
$ a -> ByteString
forall a. ToJSON a => a -> ByteString
Aeson.encode a
a)