packages feed

evoke-0.2022.8.26: source/library/Evoke/Generator/ToJSON.hs

module Evoke.Generator.ToJSON
  ( generate,
  )
where

import qualified Data.List as List
import qualified Evoke.Constant.Module as Module
import qualified Evoke.Generator.Common as Common
import qualified Evoke.Hs as Hs
import qualified Evoke.Hsc as Hsc
import qualified Evoke.Options as Options
import qualified Evoke.Type.Constructor as Constructor
import qualified Evoke.Type.Field as Field
import qualified Evoke.Type.Type as Type
import qualified GHC.Plugins as Ghc

generate :: Common.Generator
generate _ lIdP lHsQTyVars lConDecls options srcSpan = do
  type_ <- Type.make lIdP lHsQTyVars lConDecls srcSpan
  case Type.constructors type_ of
    [_] -> pure ()
    _ -> Hsc.throwError srcSpan $ Ghc.text "requires exactly one constructor"

  modifyFieldName <-
    Common.applyAll
      <$> Options.parse (Common.fieldNameOptions srcSpan) options srcSpan

  fieldNames <-
    mapM (fromField modifyFieldName)
      . List.sort
      . fmap Field.name
      . concatMap Constructor.fields
      $ Type.constructors type_

  aeson <- Common.makeRandomModule Module.dataAeson
  monoid <- Common.makeRandomModule Module.dataMonoid
  string <- Common.makeRandomModule Module.dataString
  var1 <- Common.makeRandomVariable srcSpan "var_"
  var2 <- Common.makeRandomVariable srcSpan "var_"
  let lImportDecls =
        Hs.importDecls
          srcSpan
          [ (Module.dataAeson, aeson),
            (Module.dataMonoid, monoid),
            (Module.dataString, string)
          ]

      toPair lRdrName (occName, fieldName) =
        Hs.opApp
          srcSpan
          ( Hs.app srcSpan (Hs.qualVar srcSpan string $ Ghc.mkVarOcc "fromString")
              . Hs.lit srcSpan
              $ Hs.string fieldName
          )
          (Hs.qualVar srcSpan aeson $ Ghc.mkVarOcc ".=")
          . Hs.app srcSpan (Hs.var srcSpan $ Hs.unqual srcSpan occName)
          $ Hs.var srcSpan lRdrName

      lHsExprs lRdrName = fmap (toPair lRdrName) fieldNames

      toJSON =
        Common.makeLHsBind
          srcSpan
          (Ghc.mkVarOcc "toJSON")
          [Hs.varPat srcSpan var1]
          . Hs.app srcSpan (Hs.qualVar srcSpan aeson $ Ghc.mkVarOcc "object")
          . Hs.explicitList srcSpan
          $ lHsExprs var1

      toEncoding =
        Common.makeLHsBind
          srcSpan
          (Ghc.mkVarOcc "toEncoding")
          [Hs.varPat srcSpan var2]
          . Hs.app srcSpan (Hs.qualVar srcSpan aeson $ Ghc.mkVarOcc "pairs")
          . Hs.par srcSpan
          . Hs.app srcSpan (Hs.qualVar srcSpan monoid $ Ghc.mkVarOcc "mconcat")
          . Hs.explicitList srcSpan
          $ lHsExprs var2

      lHsDecl =
        Common.makeInstanceDeclaration
          srcSpan
          type_
          aeson
          (Ghc.mkClsOcc "ToJSON")
          [toJSON, toEncoding]

  pure (lImportDecls, [lHsDecl])

fromField ::
  (String -> Ghc.Hsc String) -> Ghc.OccName -> Ghc.Hsc (Ghc.OccName, String)
fromField modifyFieldName occName = do
  fieldName <- modifyFieldName $ Ghc.occNameString occName
  pure (occName, fieldName)