packages feed

prosidy-1.6.0.2: src/Prosidy/Internal/JSON.hs

{- |
Module      : Prosidy.Internal.JSON
Description : Orphan JSON instances to let as many modules be -XSafe as possible.
Copyright   : (c) James Alexander Feldman-Crough, 2019
License     : MPL-2.0
Maintainer  : alex@fldcr.com
-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Trustworthy #-}
module Prosidy.Internal.JSON () where

import           Prosidy.Internal.Classes
import           Prosidy.Types
import           Data.Aeson
import qualified Data.HashMap.Strict           as HM
import           Data.Text                      ( Text )
import           Control.Exception              ( displayException )

instance FromJSON Block where
    parseJSON = withObject "block" $ \o -> do
        ty <- o .: "type"
        case ty :: Text of
            "tag" -> do
                subtype <- o .: "subtype"
                case subtype :: Text of
                    "block"   -> BlockTag <$> o .: "value"
                    "literal" -> BlockLiteral <$> o .: "value"
                    _         -> fail $ "unknown tag subtype: " <> show subtype
            "paragraph" -> BlockParagraph <$> o .: "value"
            _           -> fail $ "unknown block type: " <> show ty

instance ToJSON Block where
    toEncoding b = pairs . mconcat $ case b of
        BlockLiteral t ->
            [ "type" .= ("tag" :: Text)
            , "subtype" .= ("literal" :: Text)
            , "value" .= t
            ]
        BlockParagraph p -> ["type" .= ("paragraph" :: Text), "value" .= p]
        BlockTag t ->
            [ "type" .= ("tag" :: Text)
            , "subtype" .= ("block" :: Text)
            , "value" .= t
            ]

    toJSON b = object $ case b of
        BlockLiteral t ->
            [ "type" .= ("tag" :: Text)
            , "subtype" .= ("literal" :: Text)
            , "value" .= t
            ]
        BlockParagraph p -> ["type" .= ("paragraph" :: Text), "value" .= p]
        BlockTag t ->
            [ "type" .= ("tag" :: Text)
            , "subtype" .= ("block" :: Text)
            , "value" .= t
            ]

instance FromJSON Document where
    parseJSON = withObject "Document"
        $ \o -> Document <$> o .: "metadata" <*> o .: "content"

instance ToJSON Document where
    toEncoding (Document md ct) =
        pairs $ mconcat ["metadata" .= md, "content" .= ct]

    toJSON (Document md ct) = object ["metadata" .= md, "content" .= ct]

instance FromJSON Fragment where
    parseJSON = withText "Fragment" $ pure . flip Fragment Nothing

instance ToJSON Fragment where
    toEncoding = toEncoding . fragmentText
    toJSON     = toJSON . fragmentText

instance FromJSON Inline where
    parseJSON = withObject "Inline" $ \o -> do
        ty <- o .: "type"
        case ty :: Text of
            "break" -> pure Break
            "tag"   -> InlineTag <$> o .: "value"
            "text"  -> InlineText <$> o .: "value"
            _       -> fail $ "unknown inline type: " <> show ty

instance ToJSON Inline where
    toEncoding i = pairs . mconcat $ case i of
        Break -> ["type" .= ("break" :: Text), "value" .= Null]
        InlineTag t ->
            [ "type" .= ("tag" :: Text)
            , "subtype" .= ("inline" :: Text)
            , "value" .= t
            ]
        InlineText t -> ["type" .= ("text" :: Text), "value" .= t]

    toJSON i = object $ case i of
        Break -> ["type" .= ("break" :: Text)]
        InlineTag t ->
            [ "type" .= ("tag" :: Text)
            , "subtype" .= ("inline" :: Text)
            , "value" .= t
            ]
        InlineText t -> ["type" .= ("text" :: Text), "value" .= t]

instance FromJSON Metadata where
    parseJSON = withObject "Metadata"
        $ \o -> Metadata <$> o .: "properties" <*> o .: "settings"

instance ToJSON Metadata where
    toEncoding (Metadata ps ss) =
        pairs $ mconcat ["properties" .= ps, "settings" .= ss]

    toJSON (Metadata ps ss) = object ["properties" .= ps, "settings" .= ss]

instance FromJSON Paragraph where
    parseJSON = fmap (flip Paragraph Nothing) . parseJSON

instance ToJSON Paragraph where
    toEncoding (Paragraph s _) = toEncoding s
    toJSON (Paragraph s _) = toJSON s

instance ToJSON a => ToJSON (Region a) where
    toJSON (Region md ct _) = object ["metadata" .= md, "content" .= ct]

instance FromJSON a => FromJSON (Tag a) where
    parseJSON = withObject "Tag" $ \o ->
        Tag
            <$> o
            .:  "name"
            <*> o
            .:  "metadata"
            <*> o
            .:  "content"
            <*> pure Nothing

instance ToJSON a => ToJSON (Tag a) where
    toEncoding (Tag nm md ct _) =
        pairs $ mconcat ["name" .= nm, "metadata" .= md, "content" .= ct]

    toJSON (Tag nm md ct _) =
        object ["name" .= nm, "metadata" .= md, "content" .= ct]

instance FromJSONKey Key where
    fromJSONKey =
        FromJSONKeyTextParser $ either (fail . displayException) pure . makeKey

instance (Hashable a, Eq a, ToJSONKey a) => ToJSON (Set a) where
    toJSON (Set hs) = toJSON $ foldMap (flip HM.singleton True) hs
    toEncoding (Set hs) = toEncoding $ foldMap (flip HM.singleton True) hs

instance (Hashable a, Eq a, FromJSONKey a) => FromJSON (Set a) where
    parseJSON json = do
        m <- parseJSON json
        pure . Set . HM.keysSet $ HM.filter id m