{-# language DeriveGeneric        #-}
{-# language OverloadedStrings    #-}
{-# language TypeSynonymInstances #-}
{-# language FlexibleInstances    #-}
-- | Simple interface for using AutoType inference
--   in other code generators.
--
--   Simply takes a list of Aeson values,
--   and returns a type description.
--
--   For this type description,
--   we can use function to generate an entire new module.
--
--   Note that while we can put more code in the module,
--   it is recommended to avoid multiple automatically
--   generated types in order to avoid name conflicts.
--
--   NOTE: this interface is yet unstable
module JsonToType.Nested(
    defaultImportedModules
  , generateModuleImports
  , inferType
  , CodeFrag(..)
  , TypeName
  , TypeFrag
  , ModuleImport
  , PackageName
  ) where

import Data.Aeson
import JsonToType.CodeGen.Haskell(generateModuleImports, requiredPackages, importedModules, ModuleImport)
import JsonToType.CodeGen.HaskellFormat(displaySplitTypes)
import JsonToType.Extract(extractType, unifyTypes)
import JsonToType.Split(splitTypeByLabel)
import Data.Default
import Data.Typeable
import Data.Text(Text)
import GHC.Generics

-- FIXME: general type to compose generated types
-- move to JSON Autotype as library interface?
-- * API Response Structures
type Code           = Text
type TypeName       = Text
type PackageName    = Text

-- | Generated code reference and its requirements
--   Content to embed in an autogenerated module:
--
--   * name of the reference
--   * declarations to describe it
--   * module imports necessary for declarations
--     to work
data CodeFrag a = CodeFrag
  {
    -- | Code fragment to be inserted in generated module
    forall a. CodeFrag a -> Code
codeFragCode     ::  Code
    -- | Toplevel type name to refer to
  , forall a. CodeFrag a -> a
codeFragName     ::  a
    -- | List of clauses to add to imports list
  , forall a. CodeFrag a -> [Code]
codeFragImports  :: [ModuleImport]
    -- | List of packages to add to generated package dependencies
  , forall a. CodeFrag a -> [Code]
codeFragPackages :: [PackageName]
  } deriving
      ( CodeFrag a -> CodeFrag a -> Bool
(CodeFrag a -> CodeFrag a -> Bool)
-> (CodeFrag a -> CodeFrag a -> Bool) -> Eq (CodeFrag a)
forall a. Eq a => CodeFrag a -> CodeFrag a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => CodeFrag a -> CodeFrag a -> Bool
== :: CodeFrag a -> CodeFrag a -> Bool
$c/= :: forall a. Eq a => CodeFrag a -> CodeFrag a -> Bool
/= :: CodeFrag a -> CodeFrag a -> Bool
Eq
      , Int -> CodeFrag a -> ShowS
[CodeFrag a] -> ShowS
CodeFrag a -> String
(Int -> CodeFrag a -> ShowS)
-> (CodeFrag a -> String)
-> ([CodeFrag a] -> ShowS)
-> Show (CodeFrag a)
forall a. Show a => Int -> CodeFrag a -> ShowS
forall a. Show a => [CodeFrag a] -> ShowS
forall a. Show a => CodeFrag a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> CodeFrag a -> ShowS
showsPrec :: Int -> CodeFrag a -> ShowS
$cshow :: forall a. Show a => CodeFrag a -> String
show :: CodeFrag a -> String
$cshowList :: forall a. Show a => [CodeFrag a] -> ShowS
showList :: [CodeFrag a] -> ShowS
Show
      , (forall x. CodeFrag a -> Rep (CodeFrag a) x)
-> (forall x. Rep (CodeFrag a) x -> CodeFrag a)
-> Generic (CodeFrag a)
forall x. Rep (CodeFrag a) x -> CodeFrag a
forall x. CodeFrag a -> Rep (CodeFrag a) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (CodeFrag a) x -> CodeFrag a
forall a x. CodeFrag a -> Rep (CodeFrag a) x
$cfrom :: forall a x. CodeFrag a -> Rep (CodeFrag a) x
from :: forall x. CodeFrag a -> Rep (CodeFrag a) x
$cto :: forall a x. Rep (CodeFrag a) x -> CodeFrag a
to :: forall x. Rep (CodeFrag a) x -> CodeFrag a
Generic
      , Typeable
      )

type TypeFrag = CodeFrag TypeName

instance Default TypeFrag where
  -- Minimal placeholder to use in case we cannot infer proper type
  def :: TypeFrag
def = CodeFrag {
            codeFragCode :: Code
codeFragCode     =  Code
""
          , codeFragName :: Code
codeFragName     =  Code
"Data.Aeson.Value"
          , codeFragImports :: [Code]
codeFragImports  = [Code
"qualified Data.Aeson"]
          , codeFragPackages :: [Code]
codeFragPackages = [Code
"aeson"]
          }

-- | List of modules imported for Autotyped declarations
defaultImportedModules :: [Code]
defaultImportedModules = [Code]
importedModules

-- | Given intended type name, and a list of
--   text fields with JSON, return
--   either an error, or an `EndpointResponse`
--   that allows to declare and use this type
--   in generated module.
inferType :: Text -> [Value] -> TypeFrag
inferType :: Code -> [Value] -> TypeFrag
inferType Code
typeName []         = TypeFrag
forall a. Default a => a
def
inferType Code
typeName [Value]
jsonValues =
    CodeFrag {
          codeFragImports :: [Code]
codeFragImports  = [Code]
defaultImportedModules
        , codeFragCode :: Code
codeFragCode     = Map Code Type -> Code
displaySplitTypes Map Code Type
splitTypeDescriptors
        , codeFragName :: Code
codeFragName     = Code
typeName
        , codeFragPackages :: [Code]
codeFragPackages = [Code]
requiredPackages
        }
  where
    valueTypes :: [Type]
valueTypes           = (Value -> Type) -> [Value] -> [Type]
forall a b. (a -> b) -> [a] -> [b]
map Value -> Type
extractType [Value]
jsonValues
    -- FIXME: should be <> in Typelike?
    unifiedType :: Type
unifiedType          = (Type -> Type -> Type) -> [Type] -> Type
forall a. (a -> a -> a) -> [a] -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 Type -> Type -> Type
unifyTypes [Type]
valueTypes
    splitTypeDescriptors :: Map Code Type
splitTypeDescriptors = Code -> Type -> Map Code Type
splitTypeByLabel Code
typeName Type
unifiedType