fortran-src-extras-0.3.1: src/Language/Fortran/Extras/JSON/Helpers.hs
{-# LANGUAGE OverloadedStrings #-}
{- | Helpers for defining Aeson instances for fortran-src types.
To work around dependency awkwardness, we have to write an unusual concrete
context @'ToJSON' 'SrcSpan'@. It seems to work fine.
-}
module Language.Fortran.Extras.JSON.Helpers
( toJSONAnnoMerge
, toJSONAnnoTaggedObj
, jcProd, jcProdDrop
, jcSum, jcSumDrop
, jcEnum, jcEnumDrop
, tja, gtj, gte
) where
import Data.Aeson hiding ( Value )
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Types as Aeson
import Language.Fortran.Util.Position ( SrcSpan )
import Data.Text ( Text )
import GHC.Generics ( Generic, Rep )
-- | Shortcut for writing a 'toJSON' definition for a fortran-src AST node type,
-- intended to be used for sum types.
--
-- Flat/concise version which merges all keys into the same object.
toJSONAnnoMerge
:: (ToJSON a, ToJSON SrcSpan)
=> Text -> a -> SrcSpan -> [Aeson.Pair] -> Aeson.Value
toJSONAnnoMerge t a ss m = object $
[ "anno" .= a
, "span" .= ss
, "tag" .= t ] <> m
-- | Shortcut for writing a 'toJSON' definition for a fortran-src AST node type,
-- intended to be used for sum types.
--
-- "Safe" version which approximates Aeson's default 'Data.Aeson.TaggedObject'
-- sum encoding strategy, but with two extra fields extracted out.
toJSONAnnoTaggedObj
:: (ToJSON a, ToJSON SrcSpan)
=> Text -> a -> SrcSpan -> [Aeson.Pair] -> Aeson.Value
toJSONAnnoTaggedObj t a ss m = object
[ "anno" .= a
, "span" .= ss
, "tag" .= t
, "contents" .= object m ]
-- | Shortcut for selected fortran-src AST node type 'toJSON' strategy.
tja :: (ToJSON a, ToJSON SrcSpan)
=> Text -> a -> SrcSpan -> [Aeson.Pair] -> Aeson.Value
tja = toJSONAnnoMerge
-- | Base Aeson generic deriver config for product types.
jcProd :: (String -> String) -> Aeson.Options
jcProd f = Aeson.defaultOptions
{ Aeson.rejectUnknownFields = True
, Aeson.fieldLabelModifier = Aeson.camelTo2 '_' . f
}
jcProdDrop :: String -> Aeson.Options
jcProdDrop x = jcProd (drop (length x))
-- | Base Aeson generic deriver config for sum types.
jcSum :: (String -> String) -> String -> String -> Aeson.Options
jcSum f tag contents = Aeson.defaultOptions
{ Aeson.rejectUnknownFields = True
, Aeson.constructorTagModifier = Aeson.camelTo2 '_' . f
, Aeson.sumEncoding = Aeson.TaggedObject
{ Aeson.tagFieldName = tag
, Aeson.contentsFieldName = contents
}
}
jcSumDrop :: String -> Aeson.Options
jcSumDrop x = jcSum (drop (length x)) "tag" "contents"
-- | Base Aeson generic deriver config for enum types (no fields in any cons).
jcEnum :: (String -> String) -> Aeson.Options
jcEnum f = Aeson.defaultOptions
{ Aeson.rejectUnknownFields = True
, Aeson.constructorTagModifier = Aeson.camelTo2 '_' . f
}
jcEnumDrop :: String -> Aeson.Options
jcEnumDrop = jcEnum . drop . length
-- | Shortcut for common function 'genericToJSON'
gtj :: (Generic a, Aeson.GToJSON' Aeson.Value Aeson.Zero (Rep a)) => Aeson.Options -> a -> Aeson.Value
gtj = genericToJSON
-- | Shortcut for common function 'genericToEncoding'
gte :: (Generic a, Aeson.GToJSON' Aeson.Encoding Aeson.Zero (Rep a)) => Aeson.Options -> a -> Aeson.Encoding
gte = genericToEncoding