proto-lens-protoc-0.7.0.0: app/protoc-gen-haskell.hs
-- Copyright 2016 Google Inc. All Rights Reserved.
--
-- Use of this source code is governed by a BSD-style
-- license that can be found in the LICENSE file or at
-- https://developers.google.com/open-source/licenses/bsd
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
module Main where
import qualified Data.ByteString as B
import Data.Map.Strict ((!))
#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup ((<>))
#endif
import qualified Data.Set as Set
import qualified Data.Text as T
import Data.Text (Text, pack)
import Data.ProtoLens (defMessage, decodeMessage, encodeMessage)
-- Force the use of the Reflected API when decoding DescriptorProto
-- so that we can run the test suite against the Generated API.
-- TODO: switch back to Data.ProtoLens.Encoding once the Generated encoding is
-- good enough.
import Lens.Family2
import Proto.Google.Protobuf.Compiler.Plugin
( CodeGeneratorRequest
, CodeGeneratorResponse
)
import Proto.Google.Protobuf.Descriptor (FileDescriptorProto)
import System.Environment (getProgName)
import System.Exit (exitWith, ExitCode(..))
import System.IO as IO
import Data.ProtoLens.Compiler.Generate.Commented (CommentedModule, getModuleName)
import Data.ProtoLens.Compiler.Generate
import Data.ProtoLens.Compiler.Plugin
import DynFlags (DynFlags, getDynFlags)
import GHC (runGhc)
import GhcMonad (liftIO)
import GHC.Paths (libdir)
import GHC.SourceGen.Pretty (showPpr)
main :: IO ()
main = do
contents <- B.getContents
progName <- getProgName
case decodeMessage contents of
Left e -> IO.hPutStrLn stderr e >> exitWith (ExitFailure 1)
Right x -> runGhc (Just libdir) $ do
dflags <- getDynFlags
liftIO $ B.putStr $ encodeMessage $
makeResponse dflags progName x
makeResponse :: DynFlags -> String -> CodeGeneratorRequest -> CodeGeneratorResponse
makeResponse dflags prog request = let
outputFiles = generateFiles dflags header
(request ^. #protoFile)
(request ^. #fileToGenerate)
header :: FileDescriptorProto -> Text
header f = "{- This file was auto-generated from "
<> (f ^. #name)
<> " by the " <> pack prog <> " program. -}\n"
in defMessage
& #file .~ [ defMessage
& #name .~ outputName
& #content .~ outputContent
| (outputName, outputContent) <- outputFiles
]
generateFiles :: DynFlags -> (FileDescriptorProto -> Text)
-> [FileDescriptorProto] -> [ProtoFileName] -> [(Text, Text)]
generateFiles dflags header files toGenerate = let
filesByName = analyzeProtoFiles files
-- The contents of the generated Haskell file for a given .proto file.
modulesToBuild :: ProtoFile -> [CommentedModule]
modulesToBuild f = let
deps = descriptor f ^. #dependency
imports = Set.toAscList $ Set.fromList
$ map (haskellModule . (filesByName !)) deps
in generateModule (haskellModule f) (descriptor f) imports
(publicImports f)
(definitions f)
(collectEnvFromDeps deps filesByName)
(services f)
in [ ( moduleFilePath $ pack $ showPpr dflags (getModuleName modul)
, header (descriptor f) <> pack (showPpr dflags modul)
)
| fileName <- toGenerate
, let f = filesByName ! fileName
, modul <- modulesToBuild f
]
moduleFilePath :: Text -> Text
moduleFilePath n = T.replace "." "/" n <> ".hs"