{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
module Data.Aeson.AutoType.CodeGen.Haskell(
writeHaskellModule
, runHaskellModule
, runHaskellModuleStrict
, defaultHaskellFilename
) where
import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import Data.Text hiding (unwords)
import qualified Data.HashMap.Strict as Map
import Control.Arrow (first)
import Control.Exception (assert)
import Data.Monoid ((<>))
import System.FilePath
import System.IO
import System.Process (system)
import qualified System.Environment (lookupEnv)
import System.Exit (ExitCode)
import Data.Aeson.AutoType.Format
import Data.Aeson.AutoType.Type
import Data.Aeson.AutoType.CodeGen.Generic(src)
import Data.Aeson.AutoType.CodeGen.HaskellFormat
import Data.Aeson.AutoType.Util
defaultHaskellFilename :: FilePath
defaultHaskellFilename = "JSONTypes.hs"
header :: Text -> Text
header moduleName = [src|
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
module |] <> capitalize moduleName <> [src| where
import System.Exit (exitFailure, exitSuccess)
import System.IO (stderr, hPutStrLn)
import qualified Data.ByteString.Lazy.Char8 as BSL
import System.Environment (getArgs)
import Control.Monad (forM_, mzero, join)
import Control.Applicative
import Data.Aeson.AutoType.Alternative
import Data.Aeson(eitherDecode, Value(..), FromJSON(..), ToJSON(..),
pairs,
(.:), (.:?), (.=), object)
import Data.Monoid((<>))
import Data.Text (Text)
import qualified GHC.Generics
|]
epilogue :: Text -> Text
epilogue toplevelName = [src|
parse :: FilePath -> IO |] <> toplevelName <> [src|
parse filename = do
input <- BSL.readFile filename
case eitherDecode input of
Left err -> fatal $ case (eitherDecode input :: Either String Value) of
Left err -> "Invalid JSON file: " ++ filename ++ " ++ err"
Right _ -> "Mismatched JSON value from file: " ++ filename
++ "\n" ++ err
Right r -> return (r :: |] <> toplevelName <> ")" <> [src|
where
fatal :: String -> IO a
fatal msg = do hPutStrLn stderr msg
exitFailure
main :: IO ()
main = do
filenames <- getArgs
forM_ filenames (\f -> parse f >>= (\p -> p `seq` putStrLn $ "Successfully parsed " ++ f))
exitSuccess
|]
writeHaskellModule :: FilePath -> Text -> Map.HashMap Text Type -> IO ()
writeHaskellModule outputFilename toplevelName types =
withFileOrHandle outputFilename WriteMode stdout $ \hOut ->
assert (extension == ".hs") $ do
Text.hPutStrLn hOut $ header $ Text.pack moduleName
Text.hPutStrLn hOut $ displaySplitTypes types
Text.hPutStrLn hOut $ epilogue toplevelName
where
(moduleName, extension) =
first normalizeTypeName' $
splitExtension $
if outputFilename == "-"
then defaultHaskellFilename
else outputFilename
normalizeTypeName' = Text.unpack . normalizeTypeName . Text.pack
runHaskellModule :: [String] -> IO ExitCode
runHaskellModule arguments = do
maybeStack <- System.Environment.lookupEnv "STACK_EXEC"
maybeCabal <- System.Environment.lookupEnv "CABAL_SANDBOX_CONFIG"
let execPrefix | Just stackExec <- maybeStack = [stackExec, "runghc"]
| Just _ <- maybeCabal = ["cabal", "exec", "runghc"]
| otherwise = ["runghc"]
putStrLn $ "Running Haskell module: " ++ show execPrefix ++ show arguments
system $ Prelude.unwords $ execPrefix ++ arguments
runHaskellModuleStrict :: [String] -> IO ExitCode
runHaskellModuleStrict = runHaskellModule . ("-Wall":) . ("-Werror":)