packages feed

proto-lens-protoc 0.2.2.3 → 0.3.0.0

raw patch · 9 files changed

+1015/−339 lines, 9 filesdep +deepseqdep +prettydep +proto-lens-protocdep −proto-lens-descriptorsdep ~haskell-src-extsdep ~lens-labelsdep ~proto-lensPVP ok

version bump matches the API change (PVP)

Dependencies added: deepseq, pretty, proto-lens-protoc

Dependencies removed: proto-lens-descriptors

Dependency ranges changed: haskell-src-exts, lens-labels, proto-lens

API changes (from Hackage documentation)

- Data.ProtoLens.Compiler.Combinators: module' :: ModuleName -> [ModulePragma] -> [ImportDecl ()] -> [Decl] -> Module
- Data.ProtoLens.Compiler.Combinators: type Module = Module ()
+ Data.ProtoLens.Compiler.Combinators: CommentedDecl :: (Maybe String) -> Decl -> CommentedDecl
+ Data.ProtoLens.Compiler.Combinators: Module :: ModuleName -> (Maybe [ExportSpec]) -> [ModulePragma] -> [ImportDecl ()] -> [CommentedDecl] -> Module
+ Data.ProtoLens.Compiler.Combinators: commented :: String -> Decl -> CommentedDecl
+ Data.ProtoLens.Compiler.Combinators: data CommentedDecl
+ Data.ProtoLens.Compiler.Combinators: data Module
+ Data.ProtoLens.Compiler.Combinators: dataDeclHelper :: DataOrNew () -> Name -> [ConDecl] -> Deriving -> Decl
+ Data.ProtoLens.Compiler.Combinators: deriving' :: [QName] -> Deriving
+ Data.ProtoLens.Compiler.Combinators: exportAll :: QName -> ExportSpec
+ Data.ProtoLens.Compiler.Combinators: exportVar :: QName -> ExportSpec
+ Data.ProtoLens.Compiler.Combinators: exportWith :: QName -> [Name] -> ExportSpec
+ Data.ProtoLens.Compiler.Combinators: getModuleName :: Module -> ModuleName
+ Data.ProtoLens.Compiler.Combinators: instDeclWithTypes :: [Asst] -> InstHead -> [InstDecl ()] -> Decl
+ Data.ProtoLens.Compiler.Combinators: instMatch :: [Match] -> InstDecl ()
+ Data.ProtoLens.Compiler.Combinators: instType :: Type -> Type -> InstDecl ()
+ Data.ProtoLens.Compiler.Combinators: instance Data.String.IsString Data.ProtoLens.Compiler.Combinators.Promoted
+ Data.ProtoLens.Compiler.Combinators: modifyModuleName :: (String -> String) -> ModuleName -> ModuleName
+ Data.ProtoLens.Compiler.Combinators: newtypeDecl :: Name -> Type -> Deriving -> Decl
+ Data.ProtoLens.Compiler.Combinators: prettyPrim :: Pretty a => a -> Doc
+ Data.ProtoLens.Compiler.Combinators: prettyPrintModule :: Module -> String
+ Data.ProtoLens.Compiler.Combinators: tyParen :: Type -> Type
+ Data.ProtoLens.Compiler.Combinators: tyPromotedCon :: Promoted -> Type
+ Data.ProtoLens.Compiler.Combinators: tyPromotedList :: [Type] -> Type
+ Data.ProtoLens.Compiler.Combinators: type Deriving = Deriving ()
+ Data.ProtoLens.Compiler.Combinators: type ExportSpec = ExportSpec ()
+ Data.ProtoLens.Compiler.Combinators: type Promoted = Promoted ()
+ Data.ProtoLens.Compiler.Combinators: uncommented :: Decl -> CommentedDecl
+ Data.ProtoLens.Compiler.Definitions: MethodInfo :: Text -> Text -> Text -> Text -> Bool -> Bool -> MethodInfo
+ Data.ProtoLens.Compiler.Definitions: ServiceInfo :: Text -> Text -> [MethodInfo] -> ServiceInfo
+ Data.ProtoLens.Compiler.Definitions: [casePrismName] :: OneofCase -> Name
+ Data.ProtoLens.Compiler.Definitions: [enumUnrecognizedName] :: EnumInfo n -> n
+ Data.ProtoLens.Compiler.Definitions: [enumUnrecognizedValueName] :: EnumInfo n -> n
+ Data.ProtoLens.Compiler.Definitions: [messageUnknownFields] :: MessageInfo n -> Name
+ Data.ProtoLens.Compiler.Definitions: [methodClientStreaming] :: MethodInfo -> Bool
+ Data.ProtoLens.Compiler.Definitions: [methodIdent] :: MethodInfo -> Text
+ Data.ProtoLens.Compiler.Definitions: [methodInput] :: MethodInfo -> Text
+ Data.ProtoLens.Compiler.Definitions: [methodName] :: MethodInfo -> Text
+ Data.ProtoLens.Compiler.Definitions: [methodOutput] :: MethodInfo -> Text
+ Data.ProtoLens.Compiler.Definitions: [methodServerStreaming] :: MethodInfo -> Bool
+ Data.ProtoLens.Compiler.Definitions: [serviceMethods] :: ServiceInfo -> [MethodInfo]
+ Data.ProtoLens.Compiler.Definitions: [serviceName] :: ServiceInfo -> Text
+ Data.ProtoLens.Compiler.Definitions: [servicePackage] :: ServiceInfo -> Text
+ Data.ProtoLens.Compiler.Definitions: camelCase :: Text -> Text
+ Data.ProtoLens.Compiler.Definitions: collectServices :: FileDescriptorProto -> [ServiceInfo]
+ Data.ProtoLens.Compiler.Definitions: data MethodInfo
+ Data.ProtoLens.Compiler.Definitions: data ServiceInfo
+ Data.ProtoLens.Compiler.Definitions: definedType :: Text -> Env QName -> Definition QName
+ Data.ProtoLens.Compiler.Generate: instance GHC.Show.Show Data.ProtoLens.Compiler.Generate.SyntaxType
+ Data.ProtoLens.Compiler.Plugin: [services] :: ProtoFile -> [ServiceInfo]
- Data.ProtoLens.Compiler.Combinators: ImportDecl :: l -> ModuleName l -> Bool -> Bool -> Bool -> Maybe String -> Maybe (ModuleName l) -> Maybe (ImportSpecList l) -> ImportDecl l
+ Data.ProtoLens.Compiler.Combinators: ImportDecl :: l -> ModuleName l -> Bool -> Bool -> Bool -> Maybe String -> Maybe ModuleName l -> Maybe ImportSpecList l -> ImportDecl l
- Data.ProtoLens.Compiler.Combinators: [importAs] :: ImportDecl l -> Maybe (ModuleName l)
+ Data.ProtoLens.Compiler.Combinators: [importAs] :: ImportDecl l -> Maybe ModuleName l
- Data.ProtoLens.Compiler.Combinators: [importSpecs] :: ImportDecl l -> Maybe (ImportSpecList l)
+ Data.ProtoLens.Compiler.Combinators: [importSpecs] :: ImportDecl l -> Maybe ImportSpecList l
- Data.ProtoLens.Compiler.Combinators: dataDecl :: Name -> [ConDecl] -> [QName] -> Decl
+ Data.ProtoLens.Compiler.Combinators: dataDecl :: Name -> [ConDecl] -> Deriving -> Decl
- Data.ProtoLens.Compiler.Definitions: EnumInfo :: n -> EnumDescriptorProto -> [EnumValueInfo n] -> EnumInfo n
+ Data.ProtoLens.Compiler.Definitions: EnumInfo :: n -> n -> n -> EnumDescriptorProto -> [EnumValueInfo n] -> EnumInfo n
- Data.ProtoLens.Compiler.Definitions: MessageInfo :: n -> DescriptorProto -> [FieldInfo] -> [OneofInfo] -> MessageInfo n
+ Data.ProtoLens.Compiler.Definitions: MessageInfo :: n -> DescriptorProto -> [FieldInfo] -> [OneofInfo] -> Name -> MessageInfo n
- Data.ProtoLens.Compiler.Definitions: OneofCase :: FieldInfo -> Name -> OneofCase
+ Data.ProtoLens.Compiler.Definitions: OneofCase :: FieldInfo -> Name -> Name -> OneofCase
- Data.ProtoLens.Compiler.Generate: generateModule :: ModuleName -> [ModuleName] -> SyntaxType -> ModifyImports -> Env Name -> Env QName -> Module
+ Data.ProtoLens.Compiler.Generate: generateModule :: ModuleName -> [ModuleName] -> SyntaxType -> ModifyImports -> Env Name -> Env QName -> [ServiceInfo] -> [Module]
- Data.ProtoLens.Compiler.Plugin: ProtoFile :: FileDescriptorProto -> ModuleName -> Env Name -> [ProtoFileName] -> Env QName -> ProtoFile
+ Data.ProtoLens.Compiler.Plugin: ProtoFile :: FileDescriptorProto -> ModuleName -> Env Name -> [ServiceInfo] -> [ProtoFileName] -> Env QName -> ProtoFile
- Data.ProtoLens.Compiler.Plugin: moduleNameStr :: Text -> FileDescriptorProto -> String
+ Data.ProtoLens.Compiler.Plugin: moduleNameStr :: String -> FilePath -> String
- Data.ProtoLens.Setup: defaultMainGeneratingSpecificProtos :: FilePath -> (PackageDescription -> IO [FilePath]) -> IO ()
+ Data.ProtoLens.Setup: defaultMainGeneratingSpecificProtos :: FilePath -> (LocalBuildInfo -> IO [FilePath]) -> IO ()
- Data.ProtoLens.Setup: generatingSpecificProtos :: FilePath -> (PackageDescription -> IO [FilePath]) -> UserHooks -> UserHooks
+ Data.ProtoLens.Setup: generatingSpecificProtos :: FilePath -> (LocalBuildInfo -> IO [FilePath]) -> UserHooks -> UserHooks

Files

Changelog.md view
@@ -1,6 +1,22 @@ # Changelog for `proto-lens-protoc` -## Unreleased changes+## v0.3.0.0+- Remove support for `ghc-7.10`. (#136)+- Use a `.cabal` file that's auto-generated from `hpack`. (#138)+- Separate types into their own module, apart from field lenses.+- Improve readability of `HasLens` instances. (#118)+- Add support for tracking unknown fields. (#129)+- Don't generate Haskell modules if they won't be used. (#126)+- Bundle enum pattern synonyms exports with their type. (#136)+- Split the `Message` class into separate methods. (#139)+- Refactor the `FieldDescriptorType. (#147)+- Add a case to proto3 enums for unknown values. (#137)+- Track consolidation of `proto-lens-descriptors` into `proto-lens`. (#140)+- Generate service definitions using promoted datatypes. (#154)+- Generate prisms for `oneof` message fields. (#160)+- Build with `haskell-src-exts-1.20.*`. (#170)+- Add Haddock comments to fields. (#172)+- Don't unnecessarily touch files. (#177)  ## v0.2.2.3 - Don't camel-case message names.  This reverts behavior which was added
+ app/protoc-gen-haskell.hs view
@@ -0,0 +1,97 @@+-- 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 OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}+module Main where++import qualified Data.ByteString as B+import Data.Map.Strict ((!))+import Data.Monoid ((<>))+import qualified Data.Set as Set+import qualified Data.Text as T+import Data.Text (Text, pack)+import Data.ProtoLens (decodeMessage, def, encodeMessage)+import Lens.Family2+import Proto.Google.Protobuf.Compiler.Plugin+    ( CodeGeneratorRequest+    , CodeGeneratorResponse+    )+import Proto.Google.Protobuf.Compiler.Plugin_Fields+    ( content+    , file+    , fileToGenerate+    , parameter+    , protoFile+    )+import Proto.Google.Protobuf.Descriptor (FileDescriptorProto)+import Proto.Google.Protobuf.Descriptor_Fields (name, dependency)+import System.Environment (getProgName)+import System.Exit (exitWith, ExitCode(..))+import System.IO as IO++import Data.ProtoLens.Compiler.Combinators+    ( prettyPrint+    , prettyPrintModule+    , getModuleName+    )+import Data.ProtoLens.Compiler.Generate+import Data.ProtoLens.Compiler.Plugin++main :: IO ()+main = do+    contents <- B.getContents+    progName <- getProgName+    case decodeMessage contents of+        Left e -> IO.hPutStrLn stderr e >> exitWith (ExitFailure 1)+        Right x -> B.putStr $ encodeMessage $ makeResponse progName x++makeResponse :: String -> CodeGeneratorRequest -> CodeGeneratorResponse+makeResponse prog request = let+    useReexport = case T.unpack $ request ^. parameter of+                    "" -> reexported+                    "no-reexports" -> id+                    p -> error $ "Error reading parameter: " ++ show p+    outputFiles = generateFiles useReexport 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 def & file .~ [ def & name .~ outputName+                           & content .~ outputContent+                     | (outputName, outputContent) <- outputFiles+                     ]+++generateFiles :: ModifyImports -> (FileDescriptorProto -> Text)+              -> [FileDescriptorProto] -> [ProtoFileName] -> [(Text, Text)]+generateFiles modifyImports header files toGenerate = let+  modulePrefix = "Proto"+  filesByName = analyzeProtoFiles modulePrefix files+  -- The contents of the generated Haskell file for a given .proto file.+  modulesToBuild f = let+      deps = descriptor f ^. dependency+      imports = Set.toAscList $ Set.fromList+                  [ haskellModule (filesByName ! exportName)+                  | dep <- deps+                  , exportName <- exports (filesByName ! dep)+                  ]+      in generateModule (haskellModule f) imports+             (fileSyntaxType (descriptor f))+             modifyImports+             (definitions f)+             (collectEnvFromDeps deps filesByName)+             (services f)+  in [ ( outputFilePath $ prettyPrint $ getModuleName modul+       , header (descriptor f) <> pack (prettyPrintModule modul)+       )+     | fileName <- toGenerate+     , let f = filesByName ! fileName+     , modul <- modulesToBuild f+     ]+
proto-lens-protoc.cabal view
@@ -1,98 +1,100 @@-name:                proto-lens-protoc-version:             0.2.2.3-synopsis:            Protocol buffer compiler for the proto-lens library.-description:-  Turn protocol buffer files (.proto) into Haskell files (.hs) which-  can be used with the proto-lens package.+-- This file has been generated from package.yaml by hpack version 0.20.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: 4175638cf6efe9244007b8b4992689b765c2b68967bd4c36bdbc182a92e587c4 -  The library component of this package contains compiler code (namely-  Data.ProtoLens.Compiler.*) that is not guaranteed to have stable APIs.-license:             BSD3-license-file:        LICENSE-author:              Judah Jacobson-maintainer:          proto-lens@googlegroups.com-copyright:           Google Inc.-category:            Data-build-type:          Simple-cabal-version:       >=1.21-extra-source-files:  Changelog.md+name:           proto-lens-protoc+version:        0.3.0.0+synopsis:       Protocol buffer compiler for the proto-lens library.+description:    Turn protocol buffer files (.proto) into Haskell files (.hs) which can be used with the proto-lens package.+                The library component of this package contains compiler code (namely Data.ProtoLens.Compiler.*) is not guaranteed to have stable APIs.'+category:       Data+homepage:       https://github.com/google/proto-lens#readme+bug-reports:    https://github.com/google/proto-lens/issues+author:         Judah Jacobson+maintainer:     proto-lens@googlegroups.com+copyright:      Google Inc.+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.21 --- Work around stack bug, since we don't want to build the library--- during bootstrapping:--- https://github.com/commercialhaskell/stack/issues/1406-flag only-executable-    Description: Only build the executable.  Used for bootstrapping.-    Default: False+extra-source-files:+    Changelog.md -library-  if flag(only-executable) {-      buildable: False-  } else {-    exposed-modules:-        Data.ProtoLens.Compiler.Combinators-        Data.ProtoLens.Compiler.Definitions-        Data.ProtoLens.Compiler.Generate-        Data.ProtoLens.Compiler.Plugin-        Data.ProtoLens.Setup-    default-language:  Haskell2010-    hs-source-dirs:    src-    build-depends:-          Cabal >= 1.22 && < 2.1-        , base >= 4.8 && < 4.11-        , bytestring == 0.10.*-        , containers == 0.5.*-        , data-default-class >= 0.0 && < 0.2-        , directory >= 1.2 && < 1.4-        , filepath >= 1.4 && < 1.6-        , haskell-src-exts >= 1.17 && < 1.20-        , lens-family == 1.2.*-        , lens-labels == 0.1.*-        , process >= 1.2 && < 1.7-        -- Specify a more precise version of `proto-lens`, since it depends on-        -- the exact definition of the Message class.-        , proto-lens == 0.2.2.*-        , proto-lens-descriptors == 0.2.2.*-        , text == 1.2.*-    reexported-modules:-        -- Modules that are needed by the generated Haskell files.-        -- For forwards compatibility, reexport them as new module names so that-        -- other packages don't accidentally write non-generated code that-        -- relies on these modules being reexported by proto-lens-protoc.-          Prelude as Data.ProtoLens.Reexport.Prelude-        , Data.Int as Data.ProtoLens.Reexport.Data.Int-        , Data.Word as Data.ProtoLens.Reexport.Data.Word-        , Data.ByteString as Data.ProtoLens.Reexport.Data.ByteString-        , Data.Default.Class as Data.ProtoLens.Reexport.Data.Default.Class-        , Data.Map as Data.ProtoLens.Reexport.Data.Map-        , Data.ProtoLens as Data.ProtoLens.Reexport.Data.ProtoLens-        , Data.ProtoLens.Message.Enum as Data.ProtoLens.Reexport.Data.ProtoLens.Message.Enum-        , Data.Text as Data.ProtoLens.Reexport.Data.Text-        , Lens.Family2 as Data.ProtoLens.Reexport.Lens.Family2-        , Lens.Family2.Unchecked as Data.ProtoLens.Reexport.Lens.Family2.Unchecked-        , Lens.Labels as Data.ProtoLens.Reexport.Lens.Labels-    }+source-repository head+  type: git+  location: https://github.com/google/proto-lens+  subdir: proto-lens-protoc -executable proto-lens-protoc-  main-is:  protoc-gen-haskell.hs+flag only-executable+  description: Only build the executable.  Used for bootstrapping.+  manual: False+  default: False +library+  hs-source-dirs:+      src   build-depends:-        base >= 4.8 && < 4.11-      , bytestring == 0.10.*-      , containers == 0.5.*-      , data-default-class >= 0.0 && < 0.2-      , filepath >= 1.4 && < 1.6-      , haskell-src-exts >= 1.17 && < 1.20-      , lens-family == 1.2.*-      -- Specify a more precise version of `proto-lens`, since it depends on-      -- the exact definition of the Message class.-      , proto-lens == 0.2.2.*-      , proto-lens-descriptors == 0.2.2.*-      , text == 1.2.*-  hs-source-dirs:      src-  other-modules:+      Cabal >=1.22 && <2.1+    , base >=4.8 && <4.11+    , bytestring ==0.10.*+    , containers ==0.5.*+    , data-default-class >=0.0 && <0.2+    , deepseq ==1.4.*+    , directory >=1.2 && <1.4+    , filepath >=1.4 && <1.6+    , haskell-src-exts >=1.17 && <1.21+    , lens-family ==1.2.*+    , lens-labels ==0.2.*+    , pretty ==1.1.*+    , process >=1.2 && <1.7+    , proto-lens ==0.3.*+    , text ==1.2.*+  exposed-modules:       Data.ProtoLens.Compiler.Combinators       Data.ProtoLens.Compiler.Definitions       Data.ProtoLens.Compiler.Generate       Data.ProtoLens.Compiler.Plugin+      Data.ProtoLens.Setup+  other-modules:+      Paths_proto_lens_protoc+  reexported-modules:+      Prelude as Data.ProtoLens.Reexport.Prelude,+      Data.Int as Data.ProtoLens.Reexport.Data.Int,+      Data.Word as Data.ProtoLens.Reexport.Data.Word,+      Data.ByteString as Data.ProtoLens.Reexport.Data.ByteString,+      Data.ByteString.Char8 as Data.ProtoLens.Reexport.Data.ByteString.Char8,+      Data.Default.Class as Data.ProtoLens.Reexport.Data.Default.Class,+      Data.Map as Data.ProtoLens.Reexport.Data.Map,+      Data.ProtoLens as Data.ProtoLens.Reexport.Data.ProtoLens,+      Data.ProtoLens.Service.Types as Data.ProtoLens.Reexport.Data.ProtoLens.Service.Types,+      Data.ProtoLens.Message.Enum as Data.ProtoLens.Reexport.Data.ProtoLens.Message.Enum,+      Data.Text as Data.ProtoLens.Reexport.Data.Text,+      Lens.Family2 as Data.ProtoLens.Reexport.Lens.Family2,+      Lens.Family2.Unchecked as Data.ProtoLens.Reexport.Lens.Family2.Unchecked,+      Lens.Labels as Data.ProtoLens.Reexport.Lens.Labels,+      Lens.Labels.Prism as Data.ProtoLens.Reexport.Lens.Labels.Prism,+      Text.Read as Data.ProtoLens.Reexport.Text.Read+  default-language: Haskell2010 -  default-language:    Haskell2010+executable proto-lens-protoc+  main-is: protoc-gen-haskell.hs+  hs-source-dirs:+      app+  build-depends:+      base >=4.8 && <4.11+    , bytestring ==0.10.*+    , containers ==0.5.*+    , data-default-class >=0.0 && <0.2+    , deepseq ==1.4.*+    , filepath >=1.4 && <1.6+    , haskell-src-exts >=1.17 && <1.21+    , lens-family ==1.2.*+    , proto-lens ==0.3.*+    , proto-lens-protoc+    , text ==1.2.*+  other-modules:+      Paths_proto_lens_protoc+  default-language: Haskell2010
src/Data/ProtoLens/Compiler/Combinators.hs view
@@ -7,6 +7,7 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-} -- | Some utility functions, classes and instances for nicer code generation. -- -- Re-exports simpler versions of the types and constructors from@@ -33,13 +34,20 @@ import qualified Language.Haskell.Exts.Pretty as Pretty import Language.Haskell.Exts.SrcLoc (SrcLoc, noLoc) #endif+import Text.PrettyPrint (($+$), (<+>), render, text, vcat, Doc)  #if MIN_VERSION_haskell_src_exts(1,18,0) prettyPrint :: Pretty.Pretty a => a -> String prettyPrint = Pretty.prettyPrint++prettyPrim :: Pretty.Pretty a => a -> Doc+prettyPrim = Pretty.prettyPrim #else prettyPrint :: (Functor m, Pretty.Pretty (m SrcLoc)) => m () -> String prettyPrint = Pretty.prettyPrint . fmap (const noLoc)++prettyPrim :: (Functor m, Pretty.Pretty (m SrcLoc)) => m () -> Doc+prettyPrim = Pretty.prettyPrim . fmap (const noLoc) #endif  type Asst = Syntax.Asst ()@@ -67,35 +75,68 @@ type Decl = Syntax.Decl ()  patSynSig :: Name -> Type -> Decl+#if MIN_VERSION_haskell_src_exts(1,20,0)+patSynSig n t = Syntax.PatSynSig () [n] Nothing Nothing Nothing t+#else patSynSig n t = Syntax.PatSynSig () n Nothing Nothing Nothing t+#endif  patSyn :: Pat -> Pat -> Decl patSyn p1 p2 = Syntax.PatSyn () p1 p2 Syntax.ImplicitBidirectional -dataDecl :: Name -> [ConDecl] -> [QName] -> Decl-dataDecl name conDecls derives-    = Syntax.DataDecl () (Syntax.DataType ()) Nothing+dataDecl :: Name -> [ConDecl] -> Deriving -> Decl+dataDecl = dataDeclHelper $ Syntax.DataType ()++newtypeDecl :: Name -> Type -> Deriving -> Decl+newtypeDecl name wrappedType+    = dataDeclHelper (Syntax.NewType ()) name+          [Syntax.ConDecl () name [wrappedType]]++dataDeclHelper :: Syntax.DataOrNew () -> Name -> [ConDecl] -> Deriving -> Decl+dataDeclHelper dataOrNew name conDecls derives+    = Syntax.DataDecl () dataOrNew Nothing         (Syntax.DHead () name)             [Syntax.QualConDecl () Nothing Nothing q | q <- conDecls]-        $ Just $ Syntax.Deriving ()-            [ Syntax.IRule () Nothing Nothing (Syntax.IHCon () c)-            | c <- derives-            ]+#if MIN_VERSION_haskell_src_exts(1,20,0)+        [derives]+#else+        $ Just derives+#endif +type Deriving = Syntax.Deriving ()++deriving' :: [QName] -> Deriving+deriving' classes = Syntax.Deriving ()+#if MIN_VERSION_haskell_src_exts(1,20,0)+                      Nothing+#endif+                      [ Syntax.IRule () Nothing Nothing (Syntax.IHCon () c)+                      | c <- classes+                      ]+ funBind :: [Match] -> Decl funBind = Syntax.FunBind () -instDecl :: [Asst] -> InstHead -> [[Match]] -> Decl-instDecl ctx instHead matches+instType :: Type -> Type -> Syntax.InstDecl ()+instType = Syntax.InsType ()++instMatch :: [Match] -> Syntax.InstDecl ()+instMatch m = Syntax.InsDecl () $ funBind m++instDeclWithTypes :: [Asst] -> InstHead -> [Syntax.InstDecl ()] -> Decl+instDeclWithTypes ctx instHead decls     = Syntax.InstDecl () Nothing         (Syntax.IRule () Nothing ctx' instHead)-        $ Just [Syntax.InsDecl () $ funBind m | m <- matches]+        $ Just decls   where     ctx' = case ctx of         [] -> Nothing         [c] -> Just $ Syntax.CxSingle () c         cs -> Just $ Syntax.CxTuple () cs +instDecl :: [Asst] -> InstHead -> [[Match]] -> Decl+instDecl ctx instHead = instDeclWithTypes ctx instHead . fmap instMatch+ typeSig :: [Name] -> Type -> Decl typeSig = Syntax.TypeSig () @@ -150,6 +191,8 @@ ihApp :: InstHead -> [Type] -> InstHead ihApp = foldl (Syntax.IHApp ()) +tyParen :: Type -> Type+tyParen = Syntax.TyParen ()  type Match = Syntax.Match () @@ -157,19 +200,42 @@ match :: Name -> [Pat] -> Exp -> Syntax.Match () match n ps e = Syntax.Match () n ps (Syntax.UnGuardedRhs () e) Nothing -type Module = Syntax.Module ()+-- | A hand-rolled type for modules, which allows comments on top-level+-- declarations.+data Module = Module ModuleName (Maybe [ExportSpec]) [ModulePragma]+                [Syntax.ImportDecl ()]+                [CommentedDecl] -module' :: ModuleName -> [ModulePragma] -> [Syntax.ImportDecl ()] -> [Decl] -> Module-module' modName-    = Syntax.Module ()-        (Just $ Syntax.ModuleHead () modName-                    -- no warning text-                    Nothing-                    -- no explicit exports; we export everything.-                    -- TODO: Also export public imports, taking care not to-                    -- cause a name conflict between field accessors.-                    Nothing)+-- | A declaration, along with an optional comment.+data CommentedDecl = CommentedDecl (Maybe String) Decl +uncommented :: Decl -> CommentedDecl+uncommented = CommentedDecl Nothing++commented :: String -> Decl -> CommentedDecl+commented = CommentedDecl . Just++prettyPrintModule :: Module -> String+prettyPrintModule (Module modName exports pragmas imports decls) =+    render $+        vcat (map prettyPrim pragmas)+        $+$ prettyPrim (Syntax.ModuleHead () modName+                           -- no warning text+                           Nothing+                           (Syntax.ExportSpecList () <$> exports))+        $+$ vcat (map prettyPrim imports)+        $+$ ""+        $+$ vcat (map pprintDecl decls)+  where+    pprintDecl (CommentedDecl Nothing d) = prettyPrim d+    pprintDecl (CommentedDecl (Just c) d)+        = "{- |" <+> text c <+> "-}" $+$ prettyPrim d++type ExportSpec = Syntax.ExportSpec ()++getModuleName :: Module -> ModuleName+getModuleName (Module name _ _ _ _) = name+ type ModuleName = Syntax.ModuleName () type ModulePragma = Syntax.ModulePragma () @@ -179,6 +245,26 @@ optionsGhcPragma :: String -> ModulePragma optionsGhcPragma = Syntax.OptionsPragma () (Just Syntax.GHC) +exportVar :: QName -> ExportSpec+exportVar = Syntax.EVar ()++exportAll :: QName -> ExportSpec+#if MIN_VERSION_haskell_src_exts(1,18,0)+exportAll q = Syntax.EThingWith () (Syntax.EWildcard () 0) q []+#else+exportAll = Syntax.EThingAll ()+#endif++exportWith :: QName -> [Name] -> ExportSpec+#if MIN_VERSION_haskell_src_exts(1,18,0)+exportWith q = Syntax.EThingWith ()+                    (Syntax.NoWildcard ())+                    q+                    . map (Syntax.ConName ())+#else+exportWith q = Syntax.EThingWith () q . map (Syntax.ConName ())+#endif+ type Name = Syntax.Name ()  type Pat = Syntax.Pat ()@@ -214,9 +300,20 @@ tyList :: Type -> Type tyList = Syntax.TyList () +tyPromotedList :: [Type] -> Type+tyPromotedList ts = Syntax.TyPromoted () $ Syntax.PromotedList () True ts+ tyPromotedString :: String -> Type tyPromotedString s = Syntax.TyPromoted () $ Syntax.PromotedString () s s +type Promoted = Syntax.Promoted ()++tyPromotedCon :: Promoted -> Type+tyPromotedCon = Syntax.TyPromoted ()++instance IsString Promoted where+    fromString = Syntax.PromotedCon () True . fromString+ tyForAll :: [TyVarBind] -> [Asst] -> Type -> Type tyForAll vars ctx t = Syntax.TyForall () (Just vars)                             (Just $ Syntax.CxTuple () ctx)@@ -228,7 +325,7 @@ #else tyBang = Syntax.TyBang () (Syntax.BangedTy ()) #endif- + -- | Application of a Haskell type or expression to an argument. -- For example, to represent @f x y@, you can write --@@ -252,7 +349,7 @@ -- | Whether this character belongs to an Ident (e.g., "foo") or a symbol -- (e.g., "<$>"). isIdentChar :: Char -> Bool-isIdentChar c = isAlphaNum c || c `elem` "_'"+isIdentChar c = isAlphaNum c || c `elem` ("_'" :: String)  instance IsString ModuleName where     fromString = Syntax.ModuleName ()@@ -309,3 +406,8 @@  string :: String -> Syntax.Literal () string s = Syntax.String () s (show s)++modifyModuleName :: (String -> String) -> ModuleName -> ModuleName+modifyModuleName f (Syntax.ModuleName _ unpacked) =+  Syntax.ModuleName () $ f unpacked+
src/Data/ProtoLens/Compiler/Definitions.hs view
@@ -14,6 +14,8 @@     ( Env     , Definition(..)     , MessageInfo(..)+    , ServiceInfo(..)+    , MethodInfo(..)     , FieldInfo(..)     , OneofInfo(..)     , OneofCase(..)@@ -26,7 +28,10 @@     , qualifyEnv     , unqualifyEnv     , collectDefinitions+    , collectServices     , definedFieldType+    , definedType+    , camelCase     ) where  import Data.Char (isUpper, toUpper)@@ -46,15 +51,25 @@     , EnumValueDescriptorProto     , FieldDescriptorProto     , FileDescriptorProto+    , MethodDescriptorProto+    , ServiceDescriptorProto+    )+import Proto.Google.Protobuf.Descriptor_Fields+    ( clientStreaming     , enumType     , field+    , inputType     , maybe'oneofIndex     , messageType+    , method     , name     , nestedType     , number     , oneofDecl+    , outputType     , package+    , serverStreaming+    , service     , typeName     , value     )@@ -90,8 +105,26 @@     , messageOneofFields :: [OneofInfo]       -- ^ The oneofs in this message, associated with the fields that       --   belong to them.+    , messageUnknownFields :: Name+      -- ^ The name of the Haskell field in this message that holds the+      -- unknown fields.     } deriving Functor +data ServiceInfo = ServiceInfo+    { serviceName    :: Text+    , servicePackage :: Text+    , serviceMethods :: [MethodInfo]+    }++data MethodInfo = MethodInfo+    { methodName   :: Text+    , methodIdent  :: Text+    , methodInput  :: Text+    , methodOutput :: Text+    , methodClientStreaming :: Bool+    , methodServerStreaming :: Bool+    }+ -- | Information about a single field of a proto message. data FieldInfo = FieldInfo     { fieldDescriptor  :: FieldDescriptorProto@@ -111,6 +144,8 @@     , caseConstructorName :: Name         -- ^ The constructor for building a 'oneofTypeName' from the         -- value in this field.+    , casePrismName :: Name+        -- ^ The name for building 'Prism' definition.     }  data FieldName = FieldName@@ -151,6 +186,8 @@ -- | All the information needed to define or use a proto enum type. data EnumInfo n = EnumInfo     { enumName :: n+    , enumUnrecognizedName :: n+    , enumUnrecognizedValueName :: n     , enumDescriptor :: EnumDescriptorProto     , enumValues :: [EnumValueInfo n]     } deriving Functor@@ -184,6 +221,13 @@     err = error $ "definedFieldType: Field type " ++ unpack (fd ^. typeName)                   ++ " not found in environment." +-- | Look up the Haskell name for the type of a given type.+definedType :: Text -> Env QName -> Definition QName+definedType ty = fromMaybe err . Map.lookup ty+  where+    err = error $ "definedType: Type " ++ unpack ty+                  ++ " not found in environment."+ -- | Collect all the definitions in the given file (including definitions -- nested in other messages), and assign Haskell names to them. collectDefinitions :: FileDescriptorProto -> Env Name@@ -195,6 +239,28 @@     in Map.fromList $ messageAndEnumDefs protoPrefix hsPrefix                           (fd ^. messageType) (fd ^. enumType) +collectServices :: FileDescriptorProto -> [ServiceInfo]+collectServices fd = fmap (toServiceInfo $ fd ^. package) $ fd ^. service+  where+    toServiceInfo :: Text -> ServiceDescriptorProto -> ServiceInfo+    toServiceInfo pkg sd =+        ServiceInfo+            { serviceName    = sd ^. name+            , servicePackage = pkg+            , serviceMethods = fmap toMethodInfo $ sd ^. method+            }++    toMethodInfo :: MethodDescriptorProto -> MethodInfo+    toMethodInfo md =+        MethodInfo+            { methodName   = md ^. name+            , methodIdent  = camelCase $ md ^. name+            , methodInput  = fromString . T.unpack $ md ^. inputType+            , methodOutput = fromString . T.unpack $ md ^. outputType+            , methodClientStreaming = md ^. clientStreaming+            , methodServerStreaming = md ^. serverStreaming+            }+ messageAndEnumDefs :: Text -> String -> [DescriptorProto]                    -> [EnumDescriptorProto] -> [(Text, Definition Name)] messageAndEnumDefs protoPrefix hsPrefix messages enums@@ -223,6 +289,8 @@                   map (fieldInfo hsPrefix')                       $ Map.findWithDefault [] Nothing allFields             , messageOneofFields = collectOneofFields hsPrefix' d allFields+            , messageUnknownFields =+                  fromString $ "_" ++ hsPrefix' ++ "_unknownFields"             }  fieldInfo :: String -> FieldDescriptorProto -> FieldInfo@@ -241,14 +309,18 @@                           $ Map.findWithDefault [] (Just idx)                               allFields         }-    oneofCase f = OneofCase-        { caseField = fieldInfo hsPrefix f-        , caseConstructorName =-              -- Note: oneof case constructors aren't prefixed-              -- by the oneof name; field names (even inside-              -- of a oneof) are unique within a message.-              fromString $ hsPrefix ++ hsNameUnique subdefCons (f ^. name)-        }+    oneofCase f =+        let consName = hsPrefix ++ hsNameUnique subdefCons (f ^. name)+        in OneofCase+            { caseField = fieldInfo hsPrefix f+            , caseConstructorName =+                  -- Note: oneof case constructors aren't prefixed+                  -- by the oneof name; field names (even inside+                  -- of a oneof) are unique within a message.+                  fromString consName+            , casePrismName =+                  fromString $ "_" ++ consName+            }     -- Make a name that doesn't overlap with those already defined by submessages     -- or subenums.     hsNameUnique ns n@@ -358,6 +430,8 @@     in (mkText (d ^. name)        , Enum EnumInfo             { enumName = mkHsName (d ^. name)+            , enumUnrecognizedName = mkHsName (d ^. name <> "'Unrecognized")+            , enumUnrecognizedValueName = mkHsName (d ^. name <> "'UnrecognizedValue")             , enumDescriptor = d             , enumValues = collectEnumValues mkHsName $ d ^. value             })
src/Data/ProtoLens/Compiler/Generate.hs view
@@ -6,6 +6,7 @@  -- | This module builds the actual, generated Haskell file -- for a given input .proto file.+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} module Data.ProtoLens.Compiler.Generate(     generateModule,@@ -19,7 +20,7 @@ import qualified Data.Foldable as F import qualified Data.List as List import qualified Data.Map as Map-import Data.Maybe (isNothing)+import Data.Maybe (isNothing, isJust) import Data.Monoid ((<>)) import Data.Ord (comparing) import qualified Data.Set as Set@@ -28,13 +29,17 @@ import qualified Data.Text as T import Data.Tuple (swap) import Lens.Family2 ((^.))+import Text.Printf (printf)+ import Proto.Google.Protobuf.Descriptor     ( EnumValueDescriptorProto     , FieldDescriptorProto     , FieldDescriptorProto'Label(..)     , FieldDescriptorProto'Type(..)     , FileDescriptorProto-    , defaultValue+    )+import Proto.Google.Protobuf.Descriptor_Fields+    ( defaultValue     , label     , mapEntry     , maybe'oneofIndex@@ -51,7 +56,7 @@ import Data.ProtoLens.Compiler.Definitions  data SyntaxType = Proto2 | Proto3-    deriving Eq+    deriving (Show, Eq)  fileSyntaxType :: FileDescriptorProto -> SyntaxType fileSyntaxType f = case f ^. syntax of@@ -73,33 +78,56 @@                -> ModifyImports                -> Env Name      -- ^ Definitions in this file                -> Env QName     -- ^ Definitions in the imported modules-               -> Module-generateModule modName imports syntaxType modifyImport definitions importedEnv-    = module' modName+               -> [ServiceInfo]+               -> [Module]+generateModule modName imports syntaxType modifyImport definitions importedEnv services+    = [ Module modName+                (Just $ (serviceExports ++) $ concatMap generateExports $ Map.elems definitions)+                pragmas+                (prismImport:sharedImports)+          $ (concatMap generateDecls $ Map.toList definitions)+         ++ map uncommented (concatMap (generateServiceDecls env) services)+      , Module fieldModName+                Nothing+                pragmas+                sharedImports+          . map uncommented+          $ concatMap generateFieldDecls allLensNames+      ]+  where+    fieldModName = modifyModuleName (++ "_Fields") modName+    pragmas =           [ languagePragma $ map fromString               ["ScopedTypeVariables", "DataKinds", "TypeFamilies",-               "UndecidableInstances",+               "UndecidableInstances", "GeneralizedNewtypeDeriving",                "MultiParamTypeClasses", "FlexibleContexts", "FlexibleInstances",-               "PatternSynonyms", "MagicHash", "NoImplicitPrelude"]+               "PatternSynonyms", "MagicHash", "NoImplicitPrelude",+               "DataKinds"]               -- Allow unused imports in case we don't import anything from               -- Data.Text, Data.Int, etc.           , optionsGhcPragma "-fno-warn-unused-imports"+          -- haskell-src-exts doesn't support exporting `Foo(..., A, B)`+          -- in a single entry, so we use two: `Foo(..)` and `Foo(A, B)`.+          , optionsGhcPragma "-fno-warn-duplicate-exports"           ]-          (map (modifyImport . importSimple)+    prismImport = modifyImport $ importSimple "Lens.Labels.Prism"+    sharedImports = map (modifyImport . importSimple)               [ "Prelude", "Data.Int", "Data.Word"-              , "Data.ProtoLens", "Data.ProtoLens.Message.Enum"+              , "Data.ProtoLens", "Data.ProtoLens.Message.Enum", "Data.ProtoLens.Service.Types"               , "Lens.Family2", "Lens.Family2.Unchecked", "Data.Default.Class"-              , "Data.Text",  "Data.Map" , "Data.ByteString"-              , "Lens.Labels"+              , "Data.Text",  "Data.Map", "Data.ByteString", "Data.ByteString.Char8"+              , "Lens.Labels", "Text.Read"               ]-            ++ map importSimple imports)-          (concatMap generateDecls (Map.toList definitions)-           ++ concatMap generateFieldDecls allLensNames)-  where+            ++ map importSimple imports     env = Map.union (unqualifyEnv definitions) importedEnv     generateDecls (protoName, Message m)-        = generateMessageDecls syntaxType env (stripDotPrefix protoName) m-    generateDecls (_, Enum e) = generateEnumDecls e+        = generateMessageDecls fieldModName syntaxType env (stripDotPrefix protoName) m+       ++ map uncommented (concatMap (generatePrisms env) (messageOneofFields m))+    generateDecls (_, Enum e) = map uncommented $ generateEnumDecls syntaxType e+    generateExports (Message m) = generateMessageExports m+                               ++ concatMap generatePrismExports (messageOneofFields m)+    generateExports (Enum e) = generateEnumExports syntaxType e+    serviceExports = fmap generateServiceExports services     allLensNames = F.toList $ Set.fromList         [ lensSymbol inst         | Message m <- Map.elems definitions@@ -139,18 +167,93 @@   where     m' = fromString $ "Data.ProtoLens.Reexport." ++ prettyPrint m -generateMessageDecls :: SyntaxType -> Env QName -> T.Text -> MessageInfo Name -> [Decl]-generateMessageDecls syntaxType env protoName info =+messageComment :: ModuleName -> Name -> [RecordField] -> String+messageComment fieldModName n fields = unlines+    $ ["Fields :", ""]+        ++ map item (concatMap recordFieldLenses fields)+  where+    item :: LensInstance -> String+    item l = (printf "    * '%s.%s' @:: %s@"+                 (prettyPrint fieldModName)+                 (prettyPrint $ nameFromSymbol $ lensSymbol l)+                 (prettyPrint $ "Lens'" @@ t @@ (lensFieldType l)))+    t = tyCon (unQual n)++generateMessageExports :: MessageInfo Name -> [ExportSpec]+generateMessageExports m =+    map (exportAll . unQual)+        $ messageName m : map oneofTypeName (messageOneofFields m)++generateServiceDecls :: Env QName -> ServiceInfo -> [Decl]+generateServiceDecls env si =+    -- data MyService = MyService+    [ dataDecl serverDataName+      [ recDecl serverDataName []+      ]+      $ deriving' []+    ] +++    -- instance Data.ProtoLens.Service.Types.Service MyService where+    --     type ServiceName    MyService = "myService"+    --     type ServicePackage MyService = "some.package"+    --     type ServiceMethods MyService = '["normalMethod", "streamingMethod"]+    [ instDeclWithTypes [] ("Data.ProtoLens.Service.Types.Service" `ihApp` [serverRecordType])+        [ instType ("ServiceName" @@ serverRecordType)+                 . tyPromotedString . T.unpack $ serviceName si+        , instType ("ServicePackage" @@ serverRecordType)+                 . tyPromotedString . T.unpack $ servicePackage si+        , instType ("ServiceMethods" @@ serverRecordType)+                 $ tyPromotedList+                      [ tyPromotedString . T.unpack $ methodIdent m+                      | m <- List.sortBy (comparing methodIdent) $ serviceMethods si+                      ]+        ]+    ] +++    -- instance Data.ProtoLens.Service.Types.HasMethodImpl MyService "normalMethod" where+    --     type MethodInput       MyService "normalMethod" = Foo+    --     type MethodOutput      MyService "normalMethod" = Bar+    --     type IsClientStreaming MyService "normalMethod" = 'False+    --     type IsServerStreaming MyService "normalMethod" = 'False+    [ instDeclWithTypes [] ("Data.ProtoLens.Service.Types.HasMethodImpl" `ihApp` [serverRecordType, instanceHead])+        [ instType ("MethodName" @@ serverRecordType @@ instanceHead)+                 . tyPromotedString . T.unpack $ methodName m+        , instType ("MethodInput" @@ serverRecordType @@ instanceHead)+                 . lookupType $ methodInput m+        , instType ("MethodOutput" @@ serverRecordType @@ instanceHead)+                 . lookupType $ methodOutput m+        , instType ("MethodStreamingType" @@ serverRecordType @@ instanceHead)+                 . tyPromotedCon+                 $ case (methodClientStreaming m, methodServerStreaming m) of+                     (False, False) -> "Data.ProtoLens.Service.Types.NonStreaming"+                     (True,  False) -> "Data.ProtoLens.Service.Types.ClientStreaming"+                     (False, True)  -> "Data.ProtoLens.Service.Types.ServerStreaming"+                     (True,  True)  -> "Data.ProtoLens.Service.Types.BiDiStreaming"+        ]+    | m <- serviceMethods si+    , let instanceHead = tyPromotedString (T.unpack $ methodIdent m)+    ]+  where+    serverDataName = fromString . T.unpack $ serviceName si+    serverRecordType = tyCon $ unQual serverDataName++    lookupType t = case definedType t env of+                       Message msg -> tyCon $ messageName msg+                       Enum _ -> error "Service must have a message type"+++generateMessageDecls :: ModuleName -> SyntaxType -> Env QName -> T.Text -> MessageInfo Name -> [CommentedDecl]+generateMessageDecls fieldModName syntaxType env protoName info =     -- data Bar = Bar {     --    foo :: Baz     -- }-    [ dataDecl dataName-        [recDecl dataName $-                  [ (recordFieldName f, recordFieldType f)-                  | f <- allFields-                  ]-        ]-        ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"]+    [ commented (messageComment fieldModName (messageName info) allFields)+        $ dataDecl dataName+            [recDecl dataName $+                      [ (recordFieldName f, recordFieldType f)+                      | f <- allFields+                      ]+                      ++ [(messageUnknownFields info, "Data.ProtoLens.FieldSet")]+            ]+            $ deriving' ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"]     ] ++      -- oneof field data type declarations@@ -162,25 +265,34 @@     --        }     -- haskell: data Foo'Bar = Foo'Bar'c !Prelude.Float     --                       | Foo'Bar's !Sub-    [ dataDecl (oneofTypeName oneofInfo)+    [ uncommented $ dataDecl (oneofTypeName oneofInfo)       [ conDecl consName [hsFieldType env $ fieldDescriptor f]       | c <- oneofCases oneofInfo       , let f = caseField c       , let consName = caseConstructorName c       ]-      ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"]+      $ deriving' ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"]     | oneofInfo <- messageOneofFields info     ] ++--    -- type instance (Functor f, a ~ Baz, b ~ Baz)-    --     => HasLens "foo" f Bar Bar a b where+    -- instance (HasLens' f Foo x a, HasLens' f Foo x b, a ~ b)+    --    => HasLens f Foo Foo x a b+    [ uncommented $+          instDecl [classA "Lens.Labels.HasLens'" ["f", dataType, "x", "a"],+                    equalP "a" "b"]+              ("Lens.Labels.HasLens" `ihApp`+                  ["f", dataType, dataType, "x", "a", "b"])+              [[match "lensOf" [] "Lens.Labels.lensOf'"]]+    ]+    +++    -- instance Functor f+    --     => HasLens' f Foo "foo" Bar     --   lensOf _ = ...     -- Note: for optional fields, this generates an instance both for "foo" and-    -- for "maybe'foo" (see lensInfo below).-    [ instDecl [equalP "a" t, equalP "b" t, classA "Prelude.Functor" ["f"]]-        ("Lens.Labels.HasLens" `ihApp`-            [sym, "f", dataType, dataType, "a", "b"])-            [[match "lensOf" [pWildCard] $+    -- for "maybe'foo" (see plainRecordField below).+    [ uncommented $ instDecl [classA "Prelude.Functor" ["f"]]+        ("Lens.Labels.HasLens'" `ihApp`+            ["f", dataType, sym, tyParen t])+            [[match "lensOf'" [pWildCard] $                 "Prelude.."                     @@ rawFieldAccessor (unQual $ recordFieldName li)                     @@ lensExp i]]@@ -191,7 +303,7 @@     ]     ++     -- instance Data.Default.Class.Default Bar where-    [ instDecl [] ("Data.Default.Class.Default" `ihApp` [dataType])+    [ uncommented $ instDecl [] ("Data.Default.Class.Default" `ihApp` [dataType])         -- def = Bar { _Bar_foo = 0 }         [             [ match "def" []@@ -203,23 +315,292 @@                       [ fieldUpdate (unQual $ haskellRecordFieldName $ oneofFieldName o)                             "Prelude.Nothing"                       | o <- messageOneofFields info-                      ]+                      ] +++                      [ fieldUpdate (unQual $ messageUnknownFields info)+                            "[]"]             ]         ]     -- instance Message.Message Bar where-    , instDecl [] ("Data.ProtoLens.Message" `ihApp` [dataType])-        [[match "descriptor" [] $ descriptorExpr syntaxType env protoName info]]+    , uncommented $ instDecl [] ("Data.ProtoLens.Message" `ihApp` [dataType])+        $ messageInstance syntaxType env protoName info     ]   where     dataType = tyCon $ unQual dataName     dataName = messageName info     allFields = allMessageFields syntaxType env info -generateEnumDecls :: EnumInfo Name -> [Decl]-generateEnumDecls info =+-- oneof Prism declarations+-- proto: message Foo {+--          oneof bar {+--            float c = 1;+--            Sub s = 2;+--          }+--        }+-- haskell: _Foo'C :: Prism' Bar'C Float+--          _Foo'S :: Prism' Bar'S Sub+--+--  example of the function definition for _Foo'C:+-- _Foo'C :: Lens.Prism.Prism' Bar'C Float+-- _Foo'C+--   = Lens.Prism.prism' Bar'C+--       (\ p__ ->+--          case p__ of+--              Bar'C p__val -> Prelude.Just p__val+--              _otherwise -> Prelude.Nothing)+generatePrisms :: Env QName -> OneofInfo -> [Decl]+generatePrisms env oneofInfo =+    if length cases > 1+       then concatMap (generatePrism altOtherwise) cases+       else concatMap (generatePrism mempty) cases+    where+        cases = oneofCases oneofInfo+        altOtherwise = [ alt "_otherwise" "Prelude.Nothing" ]++        -- Generate type signature+        -- e.g. Prism' Bar'C Float+        generateTypeSig f funName =+            typeSig [funName] $ "Lens.Labels.Prism.Prism'"+                                -- The oneof sum type name+                             @@ (tyCon . unQual $ oneofTypeName oneofInfo)+                                -- The field contained in the sum+                             @@ (hsFieldType env $ fieldDescriptor f)+        -- Generate function definition+        -- Prism' is constructed with Constructor for building value+        -- and Deconstructor and wrapping in Just for getting value+        generateFunDef otherwiseCase consName =+               "Lens.Labels.Prism.prism'"+               -- Sum type constructor+            @@ con (unQual consName)+               -- Case deconstruction+            @@ (lambda ["p__"] $+                    case' "p__" $+                        [ alt (pApp (unQual consName) ["p__val"])+                              ("Prelude.Just" @@ "p__val")+                        ]+                       -- We want to generate the otherwise case+                       -- depending on the amount of sum type cases there are+                       ++ otherwiseCase+               )+        generatePrism :: [Alt] -> OneofCase -> [Decl]+        generatePrism otherwiseCase oneofCase =+            let consName = caseConstructorName oneofCase+                prismName = casePrismName oneofCase+            in [ generateTypeSig (caseField oneofCase) prismName+               , funBind [ match prismName [] $ generateFunDef otherwiseCase consName ]+               ]++generatePrismExports :: OneofInfo -> [ExportSpec]+generatePrismExports = map (exportVar . unQual . casePrismName) . oneofCases++generateEnumExports :: SyntaxType -> EnumInfo Name -> [ExportSpec]+generateEnumExports syntaxType e = [exportAll n, exportWith n aliases] ++ proto3NewType+  where+    n = unQual $ enumName e+    aliases = [enumValueName v | v <- enumValues e, needsManualExport v]+    needsManualExport v = isJust (enumAliasOf v)+    proto3NewType = if syntaxType == Proto3+      then [exportVar . unQual $ enumUnrecognizedValueName e]+      else []++generateServiceExports :: ServiceInfo -> ExportSpec+generateServiceExports si = exportAll $ unQual $ fromString $ T.unpack $ serviceName si++generateEnumDecls :: SyntaxType -> EnumInfo Name -> [Decl]+generateEnumDecls Proto3 info =+    -- data FooEnum+    --     = Enum1+    --     | Enum2+    --     | FooEnum'Unrecognized !FooEnum'UnrecognizedValue+    --   deriving (Prelude.Show, Prelude.Eq, Prelude.Ord, Prelude.Read)     [ dataDecl dataName+        (  (flip conDecl [] <$> constructorNames)+        ++ [conDecl unrecognizedName [tyCon $ unQual unrecognizedValueName]]+        )+        $ deriving' ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"]++    -- newtype FooEnum'UnrecognizedValue = FooEnum'UnrecognizedValue Data.Int.Int32+    --   deriving (Prelude.Eq, Prelude.Ord, Prelude.Show, Prelude.Read)+    , newtypeDecl unrecognizedValueName+       "Data.Int.Int32"+        $ deriving' ["Prelude.Eq", "Prelude.Ord", "Prelude.Show"]++    -- instance Data.ProtoLens.MessageEnum FooEnum where+    --       maybeToEnum 0 = Prelude.Just Enum1+    --       maybeToEnum 3 = Prelude.Just Enum2+    --       maybeToEnum k+    --         = Prelude.Just+    --             (FooEnum'Unrecognized+    --               (FooEnum'UnrecognizedValue (Prelude.fromIntegral k)))+    --       showEnum (FooEnum'Unrecognized (FooEnum'UnrecognizedValue k))+    --         = Prelude.show k+    --       showEnum Foo'Enum2 = "Enum2"+    --       showEnum Foo'Enum1 = "Enum1"+    --       readEnum "Enum2a" = Prelude.Just Enum2a -- alias+    --       readEnum "Enum2" = Prelude.Just Enum2+    --       readEnum "Enum1" = Prelude.Just Enum1+    --       readEnum k = Text.Read.readMaybe k >>= maybeToEnum+    , instDecl [] ("Data.ProtoLens.MessageEnum" `ihApp` [dataType])+        [ [ match "maybeToEnum" [pLitInt k] $ "Prelude.Just" @@ con (unQual c)+          | (c, k) <- constructorNumbers+          ]+          +++          [match "maybeToEnum" ["k"]+                  $ "Prelude.Just" @@+                    (con (unQual unrecognizedName)+                      @@ (con (unQual unrecognizedValueName)+                          @@ ("Prelude.fromIntegral" @@ "k")+                         )+                    )+          ]+        , [ match "showEnum" [pApp (unQual n) []]+              $ stringExp pn+          | v <- filter (null . enumAliasOf) $ enumValues info+          , let n = enumValueName v+          , let pn = T.unpack $ enumValueDescriptor v ^. name+          ] +++          [match "showEnum" [pApp (unQual unrecognizedName)+                              [pApp (unQual unrecognizedValueName) [pVar "k"]]+                            ]+                  $ "Prelude.show" @@ "k"+          ]+        , [ match "readEnum" [stringPat pn]+              $ "Prelude.Just" @@ con (unQual n)+          | v <- enumValues info+          , let n = enumValueName v+          , let pn = T.unpack $ enumValueDescriptor v ^. name+          ] +++          [match "readEnum" [pVar "k"] $ "Prelude.>>="+                                      @@ ("Text.Read.readMaybe" @@ "k")+                                      @@ "Data.ProtoLens.maybeToEnum"]+        ]++      -- instance Bounded Foo where+      --    minBound = Foo1+      --    maxBound = FooN+      , instDecl [] ("Prelude.Bounded" `ihApp` [dataType])+          [[ match "minBound" [] $ con $ unQual minBoundName+          , match "maxBound" [] $ con $ unQual maxBoundName+          ]]++      -- instance Enum Foo where+      --    toEnum k = maybe (error ("Foo.toEnum: unknown argument for enum Foo: "+      --                                ++ show k))+      --                  id (maybeToEnum k)+      --    fromEnum Foo1 = 1+      --    fromEnum Foo2 = 2+      --    ..+      --    succ FooN = error "Foo.succ: bad argument FooN."+      --    succ Foo1 = Foo2+      --    succ Foo2 = Foo3+      --    ..+      --    pred Foo1 = error "Foo.succ: bad argument Foo1."+      --    pred Foo2 = Foo1+      --    pred Foo3 = Foo2+      --    ..+      --    enumFrom = messageEnumFrom+      --    enumFromTo = messageEnumFromTo+      --    enumFromThen = messageEnumFromThen+      --    enumFromThenTo = messageEnumFromThenTo+      , instDecl [] ("Prelude.Enum" `ihApp` [dataType])+        [[match "toEnum" ["k__"]+                  $ "Prelude.maybe" @@ errorMessageExpr @@ "Prelude.id"+                        @@ ("Data.ProtoLens.maybeToEnum" @@ "k__")]+        , [ match "fromEnum" [pApp (unQual c) []] $ litInt k+          | (c, k) <- constructorNumbers+          ]+          +++          [match "fromEnum" [pApp (unQual unrecognizedName)+                              [pApp (unQual unrecognizedValueName) [pVar "k"]]+                            ]+                  $ "Prelude.fromIntegral" @@ "k"+          ]+        , succDecl "succ" maxBoundName succPairs+        , succDecl "pred" minBoundName $ map swap succPairs+        , alias "enumFrom" "Data.ProtoLens.Message.Enum.messageEnumFrom"+        , alias "enumFromTo" "Data.ProtoLens.Message.Enum.messageEnumFromTo"+        , alias "enumFromThen" "Data.ProtoLens.Message.Enum.messageEnumFromThen"+        , alias "enumFromThenTo"+            "Data.ProtoLens.Message.Enum.messageEnumFromThenTo"+        ]++    -- instance Data.Default.Class.Default Foo where+    --   def = FirstEnumValue+    , instDecl [] ("Data.Default.Class.Default" `ihApp` [dataType])+        [[match "def" [] defaultCon]]+    -- instance Data.ProtoLens.FieldDefault Foo where+    --   fieldDefault = FirstEnumValue+    , instDecl [] ("Data.ProtoLens.FieldDefault" `ihApp` [dataType])+        [[match "fieldDefault" [] defaultCon]]+    ] +++    -- pattern Enum2a :: FooEnum+    -- pattern Enum2a = Enum2+    concat+        [ [ patSynSig aliasName dataType+          , patSyn (pVar aliasName) (pVar originalName)+          ]+        | EnumValueInfo+            { enumValueName = aliasName+            , enumAliasOf = Just originalName+            } <- enumValues info+        ]++  where+    EnumInfo { enumName = dataName+             , enumUnrecognizedName = unrecognizedName+             , enumUnrecognizedValueName = unrecognizedValueName+             , enumDescriptor = ed+             } = info+    errorMessage = "toEnum: unknown value for enum " ++ unpack (ed ^. name)+                      ++ ": "++    errorMessageExpr = "Prelude.error"+                          @@ ("Prelude.++" @@ stringExp errorMessage+                              @@ ("Prelude.show" @@ "k__"))+    alias funName implName = [match funName [] implName]++    dataType = tyCon $ unQual dataName++++    constructors :: [(Name, EnumValueDescriptorProto)]+    constructors = List.sortBy (comparing ((^. number) . snd))+                            [(n, d) | EnumValueInfo+                                { enumValueName = n+                                , enumValueDescriptor = d+                                , enumAliasOf = Nothing+                                } <- enumValues info+                            ]+    constructorNames = map fst constructors++    defaultCon = con $ unQual $ head constructorNames++    minBoundName = head constructorNames+    maxBoundName = last constructorNames++    constructorNumbers = map (second (fromIntegral . (^. number))) constructors++    succPairs = zip constructorNames $ tail constructorNames+    succDecl funName boundName thePairs =+        match funName [pApp (unQual boundName) []]+            ("Prelude.error" @@ stringExp (concat+                [ prettyPrint dataName, ".", prettyPrint funName, ": bad argument "+                , prettyPrint boundName, ". This value would be out of bounds."+                ]))+        :+        [ match funName [pApp (unQual from) []] $ con $ unQual to+        | (from, to) <- thePairs+        ]+        +++        [match funName [pWildCard]+            ("Prelude.error" @@ stringExp (concat+                [ prettyPrint dataName, ".", prettyPrint funName, ": bad argument: unrecognized value"+                ]))+        ]++generateEnumDecls Proto2 info =+    [ dataDecl dataName         [conDecl n [] | n <- constructorNames]-        ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"]+        $ deriving' ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"]     -- instance Data.Default.Class.Default Foo where     --   def = FirstEnumValue     , instDecl [] ("Data.Default.Class.Default" `ihApp` [dataType])@@ -358,18 +739,14 @@ generateFieldDecls :: Symbol -> [Decl] generateFieldDecls xStr =     -- foo :: forall x f s t a b-    --        . HasLens x f s t a b => LensLike f s t a b+    --        . HasLens f s t x a b => LensLike f s t a b     -- -- Note: `Lens.Family2.LensLike f` implies Functor f.     -- foo = lensOf (Proxy# :: Proxy# x)     [ typeSig [x]           $ tyForAll ["f", "s", "t", "a", "b"]-                  [classA "Lens.Labels.HasLens" [xSym, "f", "s", "t", "a", "b"]]+                  [classA "Lens.Labels.HasLens" ["f", "s", "t", xSym, "a", "b"]]                     $ "Lens.Family2.LensLike" @@ "f" @@ "s" @@ "t" @@ "a" @@ "b"-    , funBind [match x []-                  $ "Lens.Labels.lensOf"-                      @@ ("Lens.Labels.proxy#" @::@-                          ("Lens.Labels.Proxy#" @@ xSym))-              ]+    , funBind [match x [] $ lensOfExp xStr]     ]   where     x = nameFromSymbol xStr@@ -662,21 +1039,15 @@     setter = lambda ["_", "y__"]                 $ "Prelude.fmap" @@ con (unQual consName) @@ "y__" -descriptorExpr :: SyntaxType -> Env QName -> T.Text -> MessageInfo Name -> Exp-descriptorExpr syntaxType env protoName m-    -- let foo__field_descriptor = ...-    --     ...-    -- in Message.MessageDescriptor-    --      (Data.Map.fromList [(Tag 1, foo__field_descriptor),...])-    --      (Data.Map.fromList [("foo", foo__field_descriptor),...])-    ---    -- (Note that the two maps have the same elements but different keys.  We-    -- use the "let" expression to share elements between the two maps.)-    = let' (map (fieldDescriptorVarBind $ messageName m) $ fields)-        $ "Data.ProtoLens.MessageDescriptor"-          @@ ("Data.Text.pack" @@ stringExp (T.unpack protoName))-          @@ ("Data.Map.fromList" @@ list fieldsByTag)-          @@ ("Data.Map.fromList" @@ list fieldsByTextFormatName)+messageInstance :: SyntaxType -> Env QName -> T.Text -> MessageInfo Name -> [[Match]]+messageInstance syntaxType env protoName m =+    [ [ match "messageName" [pWildCard] $+          "Data.Text.pack" @@ stringExp (T.unpack protoName)]+    , [ match "fieldsByTag" [] $+          let' (map (fieldDescriptorVarBind $ messageName m) $ fields)+              $ "Data.Map.fromList" @@ list fieldsByTag ]+    , [ match "unknownFields" [] $ rawFieldAccessor (unQual $ messageUnknownFields m) ]+    ]   where     fieldsByTag =         [tuple@@ -686,13 +1057,6 @@                           @@ litInt (fromIntegral                                       $ fieldDescriptor f ^. number)               ]-    fieldsByTextFormatName =-        [tuple-              [ t, fieldDescriptorVar f ]-              | f <- fields-              , let t = stringExp $ T.unpack $ textFormatFieldName env-                                                    (fieldDescriptor f)-              ]     fieldDescriptorVar = var . unQual . fieldDescriptorName     fieldDescriptorName f         = nameFromSymbol $ overloadedName (plainFieldName f) <> "__field_descriptor"@@ -737,7 +1101,8 @@  fieldAccessorExpr :: SyntaxType -> Env QName -> FieldInfo -> Exp -- (PlainField Required foo), (OptionalField foo), etc...-fieldAccessorExpr syntaxType env f = accessorCon @@ var (unQual hsFieldName)+fieldAccessorExpr syntaxType env f = accessorCon @@ lensOfExp hsFieldName+   where     fd = fieldDescriptor f     accessorCon = case fd ^. label of@@ -750,19 +1115,24 @@           FieldDescriptorProto'LABEL_REPEATED               | Just (k, v) <- getMapFields env fd                   -> "Data.ProtoLens.MapField"-                         @@ con (unQual $ nameFromSymbol $ overloadedField k)-                         @@ con (unQual $ nameFromSymbol $ overloadedField v)+                         @@ lensOfExp (overloadedField k)+                         @@ lensOfExp (overloadedField v)               | otherwise -> "Data.ProtoLens.RepeatedField"                   @@ if isPackedField syntaxType fd                         then "Data.ProtoLens.Packed"                         else "Data.ProtoLens.Unpacked"     hsFieldName-        = nameFromSymbol $ case fd ^. label of+        = case fd ^. label of               FieldDescriptorProto'LABEL_OPTIONAL                   | not (isDefaultingOptional syntaxType fd)                       -> "maybe'" <> overloadedField f               _ -> overloadedField f +lensOfExp :: Symbol -> Exp+lensOfExp sym = ("Lens.Labels.lensOf"+                  @@ ("Lens.Labels.proxy#" @::@+                      ("Lens.Labels.Proxy#" @@ promoteSymbol sym)))+ overloadedField :: FieldInfo -> Symbol overloadedField = overloadedName . plainFieldName @@ -789,23 +1159,25 @@                       ]  fieldTypeDescriptorExpr :: FieldDescriptorProto'Type -> Exp-fieldTypeDescriptorExpr =-    (\n -> fromString $ "Data.ProtoLens." ++ n ++ "Field") . \t -> case t of-    FieldDescriptorProto'TYPE_DOUBLE -> "Double"-    FieldDescriptorProto'TYPE_FLOAT -> "Float"-    FieldDescriptorProto'TYPE_INT64 -> "Int64"-    FieldDescriptorProto'TYPE_UINT64 -> "UInt64"-    FieldDescriptorProto'TYPE_INT32 -> "Int32"-    FieldDescriptorProto'TYPE_FIXED64 -> "Fixed64"-    FieldDescriptorProto'TYPE_FIXED32 -> "Fixed32"-    FieldDescriptorProto'TYPE_BOOL -> "Bool"-    FieldDescriptorProto'TYPE_STRING -> "String"-    FieldDescriptorProto'TYPE_GROUP -> "Group"-    FieldDescriptorProto'TYPE_MESSAGE -> "Message"-    FieldDescriptorProto'TYPE_BYTES -> "Bytes"-    FieldDescriptorProto'TYPE_UINT32 -> "UInt32"-    FieldDescriptorProto'TYPE_ENUM -> "Enum"-    FieldDescriptorProto'TYPE_SFIXED32 -> "SFixed32"-    FieldDescriptorProto'TYPE_SFIXED64 -> "SFixed64"-    FieldDescriptorProto'TYPE_SINT32 -> "SInt32"-    FieldDescriptorProto'TYPE_SINT64 -> "SInt64"+fieldTypeDescriptorExpr = \case+    FieldDescriptorProto'TYPE_DOUBLE -> mk "ScalarField" "DoubleField"+    FieldDescriptorProto'TYPE_FLOAT -> mk "ScalarField" "FloatField"+    FieldDescriptorProto'TYPE_INT64 -> mk "ScalarField" "Int64Field"+    FieldDescriptorProto'TYPE_UINT64 -> mk "ScalarField" "UInt64Field"+    FieldDescriptorProto'TYPE_INT32 -> mk "ScalarField" "Int32Field"+    FieldDescriptorProto'TYPE_FIXED64 -> mk "ScalarField" "Fixed64Field"+    FieldDescriptorProto'TYPE_FIXED32 -> mk "ScalarField" "Fixed32Field"+    FieldDescriptorProto'TYPE_BOOL -> mk "ScalarField" "BoolField"+    FieldDescriptorProto'TYPE_STRING -> mk "ScalarField" "StringField"+    FieldDescriptorProto'TYPE_GROUP -> mk "MessageField" "GroupType"+    FieldDescriptorProto'TYPE_MESSAGE -> mk "MessageField" "MessageType"+    FieldDescriptorProto'TYPE_BYTES -> mk "ScalarField" "BytesField"+    FieldDescriptorProto'TYPE_UINT32 -> mk "ScalarField" "UInt32Field"+    FieldDescriptorProto'TYPE_ENUM -> mk "ScalarField" "EnumField"+    FieldDescriptorProto'TYPE_SFIXED32 -> mk "ScalarField" "SFixed32Field"+    FieldDescriptorProto'TYPE_SFIXED64 -> mk "ScalarField" "SFixed64Field"+    FieldDescriptorProto'TYPE_SINT32 -> mk "ScalarField" "SInt32Field"+    FieldDescriptorProto'TYPE_SINT64 -> mk "ScalarField" "SInt64Field"+  where+    mk x y = fromString ("Data.ProtoLens." ++ x)+              @@ fromString ("Data.ProtoLens." ++ y)
src/Data/ProtoLens/Compiler/Plugin.hs view
@@ -26,8 +26,8 @@ import qualified Data.Text as T import Data.Text (Text) import Lens.Family2-import Proto.Google.Protobuf.Descriptor-    (FileDescriptorProto, name, dependency, publicDependency)+import Proto.Google.Protobuf.Descriptor (FileDescriptorProto)+import Proto.Google.Protobuf.Descriptor_Fields (name, dependency, publicDependency) import System.FilePath (dropExtension, splitDirectories)  @@ -41,6 +41,7 @@     { descriptor :: FileDescriptorProto     , haskellModule :: ModuleName     , definitions :: Env Name+    , services :: [ServiceInfo]     -- | The names of proto files exported (transitively, via "import public"     -- decl) by this file.     , exports :: [ProtoFileName]@@ -57,6 +58,7 @@     moduleNames = fmap (moduleName modulePrefix) filesByName     -- The definitions in each input proto file, indexed by filename.     definitionsByName = fmap collectDefinitions filesByName+    servicesByName = fmap collectServices filesByName     -- The exports from each .proto file (including any "public import"     -- dependencies), as they appear to other modules that are importing them;     -- i.e., qualified by module name.@@ -68,6 +70,7 @@         { descriptor = f         , haskellModule = moduleNames ! n         , definitions = definitionsByName ! n+        , services = servicesByName ! n         , exports = exportsByName ! n         , exportedEnv = exportedEnvs ! n         }@@ -85,22 +88,23 @@  -- | Get the Haskell 'ModuleName' corresponding to a given .proto file. moduleName :: Text -> FileDescriptorProto -> ModuleName-moduleName modulePrefix fd = fromString (moduleNameStr modulePrefix fd)+moduleName modulePrefix fd+      = fromString $ moduleNameStr (T.unpack modulePrefix) (T.unpack $ fd ^. name)  -- | Get the Haskell module name corresponding to a given .proto file.-moduleNameStr :: Text -> FileDescriptorProto -> String-moduleNameStr prefix fd = fixModuleName rawModuleName+moduleNameStr :: String -> FilePath -> String+moduleNameStr prefix path = fixModuleName rawModuleName   where-    path = fd ^. name     fixModuleName "" = ""     -- Characters allowed in Bazel filenames but not in module names:     fixModuleName ('.':c:cs) = '.' : toUpper c : fixModuleName cs     fixModuleName ('_':c:cs) = toUpper c : fixModuleName cs     fixModuleName ('-':c:cs) = toUpper c : fixModuleName cs     fixModuleName (c:cs) = c : fixModuleName cs-    rawModuleName = intercalate "." $ (T.unpack prefix :)-                        $ splitDirectories $ dropExtension-                        $ T.unpack path+    rawModuleName = intercalate "."+                        . (prefix :)+                        . splitDirectories $ dropExtension+                        $ path  -- | Given a list of .proto files (topologically sorted), determine which -- files' definitions are exported by which files.
src/Data/ProtoLens/Setup.hs view
@@ -14,6 +14,7 @@ -- -- See @README.md@ for instructions on how to use proto-lens with Cabal. {-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-} module Data.ProtoLens.Setup     ( defaultMainGeneratingProtos     , defaultMainGeneratingSpecificProtos@@ -23,35 +24,49 @@     , generateProtos     ) where -#if __GLASGOW_HASKELL__ < 709-import Data.Functor ((<$>))+import Control.DeepSeq (force)+import Control.Monad (filterM, forM_, guard, when)+#if MIN_VERSION_Cabal(2,0,0)+import qualified Data.Map as Map #endif--import Control.Monad (filterM, forM_, when)+import qualified Data.ByteString as BS+import Data.Maybe (maybeToList)+import qualified Data.Set as Set+import Distribution.ModuleName (ModuleName)+import qualified Distribution.ModuleName as ModuleName import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo import Distribution.PackageDescription     ( PackageDescription(..)     , benchmarkBuildInfo+    , benchmarkName     , buildInfo+    , exeName+    , exposedModules     , extraSrcFiles     , hsSourceDirs     , libBuildInfo+    , otherModules     , testBuildInfo+    , testBuildInfo+    , testName     ) import qualified Distribution.Simple.BuildPaths as BuildPaths import Distribution.Simple.InstallDirs (datadir) import Distribution.Simple.LocalBuildInfo     ( LocalBuildInfo(..)     , absoluteInstallDirs+    , ComponentName(..)     , componentPackageDeps #if MIN_VERSION_Cabal(2,0,0)     , allComponentsInBuildOrder+    , componentNameMap #endif     ) import qualified Distribution.Simple.PackageIndex as PackageIndex import Distribution.Simple.Setup (fromFlag, copyDest, copyVerbosity) import Distribution.Simple.Utils     ( createDirectoryIfMissingVerbose+    , getDirectoryContentsRecursive     , installOrdinaryFile     , matchFileGlob     )@@ -66,23 +81,32 @@     , equalFilePath     , isRelative     , makeRelative-    , takeExtension     , takeDirectory+    , takeExtension     ) import System.Directory     ( createDirectoryIfMissing     , doesDirectoryExist+    , doesFileExist     , findExecutable     , removeDirectoryRecursive+    , renameFile     ) import System.IO (hPutStrLn, stderr) import System.Process (callProcess) +import qualified Data.ProtoLens.Compiler.Plugin as Plugin+ -- | This behaves the same as 'Distribution.Simple.defaultMain', but--- auto-generates Haskell files from the .proto files listed in--- the @.cabal@ file under @extra-source-files@ which are located under the--- given root directory.+-- auto-generates Haskell files from .proto files which are: --+-- * Listed in the @.cabal@ file under @extra-source-files@,+--+-- * Located under the given root directory, and+--+-- * Correspond to a module (@"Proto.*"@) in `exposed-modules` or+-- `other-modules` of some component in the @.cabal@ file.+-- -- Writes the generated files to the autogen directory (@dist\/build\/autogen@ -- for Cabal, and @.stack-work\/dist\/...\/build\/autogen@ for stack). --@@ -103,7 +127,7 @@ -- Throws an exception if the @proto-lens-protoc@ executable is not on the PATH. defaultMainGeneratingSpecificProtos     :: FilePath -- ^ The root directory under which .proto files can be found.-    -> (PackageDescription -> IO [FilePath])+    -> (LocalBuildInfo -> IO [FilePath])     -- ^ A function to return a list of .proto files. Takes the Cabal package     -- description as input. Non-absolute paths are treated as relative to the     -- provided root directory.@@ -113,9 +137,15 @@     $ generatingSpecificProtos root getProtos simpleUserHooks  -- | Augment the given 'UserHooks' to auto-generate Haskell files from the--- .proto files listed in the @.cabal@ file under @extra-source-files@ which--- are located under the given root directory.+-- .proto files which are: --+-- * Listed in the @.cabal@ file under @extra-source-files@,+--+-- * Located under the given root directory, and+--+-- * Correspond to a module (@"Proto.*"@) in `exposed-modules` or+-- `other-modules` of some component in the @.cabal@ file.+-- -- Writes the generated files to the autogen directory (@dist\/build\/autogen@ -- for Cabal, and @.stack-work\/dist\/...\/build\/autogen@ for stack). --@@ -125,14 +155,20 @@     -> UserHooks -> UserHooks generatingProtos root = generatingSpecificProtos root getProtos   where-    getProtos p = do+    getProtos l = do       -- Replicate Cabal's own logic for parsing file globs.-      files <- concat <$> mapM matchFileGlob (extraSrcFiles p)-      pure $ map (makeRelative root)-           $ filter (isSubdirectoryOf root)-           $ filter (\f -> takeExtension f == ".proto")-               files+      files <- concat <$> mapM matchFileGlob (extraSrcFiles $ localPkgDescr l)+      let activeModules = Set.fromList $ collectActiveModules l+      pure . filter (\f -> relativeFileToProtoModule f+                            `Set.member` activeModules)+           . filter (\f -> takeExtension f == ".proto")+           . map (makeRelative root)+           . filter (isSubdirectoryOf root)+           $ files+    relativeFileToProtoModule+        = ModuleName.fromString . Plugin.moduleNameStr "Proto" + -- | Augment the given 'UserHooks' to auto-generate Haskell files from the -- .proto files returned by a function @getProtos@. --@@ -142,46 +178,77 @@ -- Throws an exception if the @proto-lens-protoc@ executable is not on the PATH. generatingSpecificProtos     :: FilePath -- ^ The root directory under which .proto files can be found.-    -> (PackageDescription -> IO [FilePath])+    -> (LocalBuildInfo -> IO [FilePath])     -- ^ A function to return a list of .proto files. Takes the Cabal package     -- description as input. Non-absolute paths are treated as relative to the     -- provided root directory.     -> UserHooks -> UserHooks generatingSpecificProtos root getProtos hooks = hooks-    { buildHook = \p l h f -> generate p l >> buildHook hooks p l h f-    , haddockHook = \p l h f -> generate p l >> haddockHook hooks p l h f-    , replHook = \p l h f args -> generate p l >> replHook hooks p l h f args+    { buildHook = \p l h f -> generate l >> buildHook hooks p l h f+    , haddockHook = \p l h f -> generate l >> haddockHook hooks p l h f+    , replHook = \p l h f args -> generate l >> replHook hooks p l h f args     , sDistHook = \p maybe_l h f -> case maybe_l of             Nothing -> error "Can't run protoc; run 'cabal configure' first."             Just l -> do-                        generate p l+                        generate l                         sDistHook hooks (fudgePackageDesc l p) maybe_l h f     , postCopy = \a flags pkg lbi -> do                   let verb = fromFlag $ copyVerbosity flags                   let destDir = datadir (absoluteInstallDirs pkg lbi                                              $ fromFlag $ copyDest flags)                               </> protoLensImportsPrefix-                  getProtos pkg >>= copyProtosToDataDir verb root destDir+                  getProtos lbi >>= copyProtosToDataDir verb root destDir                   postCopy hooks a flags pkg lbi     }   where-    generate p l = getProtos p >>= generateSources root l+    generate l = getProtos l >>= generateSources root l  -- | Generate Haskell source files for the given input .proto files. generateSources :: FilePath -- ^ The root directory                 -> LocalBuildInfo                 -> [FilePath] -- ^ Proto files relative to the root directory.                 -> IO ()+generateSources _ _ [] = return () generateSources root l files = do     -- Collect import paths from build-depends of this package.     importDirs <- filterM doesDirectoryExist                      [ InstalledPackageInfo.dataDir info </> protoLensImportsPrefix                      | info <- collectDeps l                      ]-    generateProtosWithImports (root : importDirs) (autogenModulesDir l)+    -- Generate .hs files into a temporary directory, then move them over+    -- to the target (autogen) directory only if they are different from+    -- what's already there. This way, we don't needlessly touch the generated+    -- .hs files when nothing changes, and thus don't needlessly make GHC+    -- recompile them (as it considers their modification times for that).+    let tmpAutogenModulesDir = autogenModulesDir l ++ "-protoc-tmpOutDir"+    -- Generate .hs files into temp dir.+    generateProtosWithImports (root : importDirs) tmpAutogenModulesDir                               -- Applying 'root </>' does nothing if the path is already                               -- absolute.                               (map (root </>) files)+    -- Discover generated files.+    -- `getDirectoryContentsRecursive` is lazy IO, so we `force` through+    -- the list to make it strict hereinafter.+    !generatedFiles <- force <$> getDirectoryContentsRecursive tmpAutogenModulesDir+    -- Move files to autogen dir only if file contents are different.+    forM_ generatedFiles $ \pathRelativeToTmpDir -> do+        let sourcePath = tmpAutogenModulesDir </> pathRelativeToTmpDir+        let targetPath = autogenModulesDir l </> pathRelativeToTmpDir+        identical <- do+            targetExists <- doesFileExist targetPath+            if not targetExists+                then return False+                else do+                    -- This could be done in a streaming fashion,+                    -- but since the .hs files usually easily fit+                    -- into RAM, this is OK.+                    sourceContents <- BS.readFile sourcePath+                    targetContents <- BS.readFile targetPath+                    return (sourceContents == targetContents)+        -- Do the move if necessary.+        when (not identical) $ do+            createDirectoryIfMissing True (takeDirectory targetPath)+            renameFile sourcePath targetPath  -- | Copy each .proto file into the installed "data-dir" path, -- so that it can be included by other packages that depend on this one.@@ -296,6 +363,29 @@             hPutStrLn stderr sep             error $ "Missing executable " ++ show name +-- | Collect all the module names that we need to build.+-- For example: only include test-suites if we're building with tests enabled+-- (e.g., `stack test` vs `stack build`).+collectActiveModules :: LocalBuildInfo -> [ModuleName]+collectActiveModules l = let+    in (activeLib >>= exposedModules)+        ++ concatMap otherModules+            (concat+                [ libBuildInfo <$> activeLib+                , buildInfo <$> activeExes+                , testBuildInfo <$> activeTests+                , benchmarkBuildInfo <$> activeBenchmarks+                ])+  where+    p = localPkgDescr l+    activeLib = guard (active CLibName) >> maybeToList (library p)+    activeExes = filter (active . CExeName . exeName) $ executables p+    activeTests = filter (active . CTestName . testName) $ testSuites p+    activeBenchmarks = filter (active . CBenchName . benchmarkName)+                          $ benchmarks p+    comps = Set.fromList $ allComponentNames l+    active = (`Set.member` comps)+ ------------------------------------------------------- -- Compatibility layer between Cabal-1.* and Cabal-2.* @@ -312,6 +402,14 @@     (_, c ,_) <- componentsConfigs l     (_, i) <- componentPackageDeps c     PackageIndex.lookupSourcePackageId (installedPkgs l) i+#endif++-- | All the components that will be built by this Cabal command.+allComponentNames :: LocalBuildInfo -> [ComponentName]+#if MIN_VERSION_Cabal(2,0,0)+allComponentNames l = Map.keys $ componentNameMap l+#else+allComponentNames l = [c | (c, _, _) <- componentsConfigs l] #endif  -- | Get the package-level "autogen" directory where we're putting the
− src/protoc-gen-haskell.hs
@@ -1,89 +0,0 @@--- 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 OverloadedStrings #-}-{-# LANGUAGE PatternSynonyms #-}-module Main where--import qualified Data.ByteString as B-import Data.Map.Strict ((!))-import Data.Monoid ((<>))-import qualified Data.Set as Set-import qualified Data.Text as T-import Data.Text (Text, pack)-import Data.ProtoLens (decodeMessage, def, encodeMessage)-import Lens.Family2-import Proto.Google.Protobuf.Compiler.Plugin-    ( CodeGeneratorRequest-    , CodeGeneratorResponse-    , content-    , file-    , fileToGenerate-    , parameter-    , protoFile-    )-import Proto.Google.Protobuf.Descriptor-    (FileDescriptorProto, name, dependency)-import System.Environment (getProgName)-import System.Exit (exitWith, ExitCode(..))-import System.IO as IO--import Data.ProtoLens.Compiler.Combinators (prettyPrint)-import Data.ProtoLens.Compiler.Generate-import Data.ProtoLens.Compiler.Plugin--main :: IO ()-main = do-    contents <- B.getContents-    progName <- getProgName-    case decodeMessage contents of-        Left e -> IO.hPutStrLn stderr e >> exitWith (ExitFailure 1)-        Right x -> B.putStr $ encodeMessage $ makeResponse progName x--makeResponse :: String -> CodeGeneratorRequest -> CodeGeneratorResponse-makeResponse prog request = let-    useReexport = case T.unpack $ request ^. parameter of-                    "" -> reexported-                    "no-reexports" -> id-                    p -> error $ "Error reading parameter: " ++ show p-    outputFiles = generateFiles useReexport 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 def & file .~ [ def & name .~ outputName-                           & content .~ outputContent-                     | (outputName, outputContent) <- outputFiles-                     ]---generateFiles :: ModifyImports -> (FileDescriptorProto -> Text)-              -> [FileDescriptorProto] -> [ProtoFileName] -> [(Text, Text)]-generateFiles modifyImports header files toGenerate = let-  modulePrefix = "Proto"-  filesByName = analyzeProtoFiles modulePrefix files-  -- The contents of the generated Haskell file for a given .proto file.-  buildFile f = let-      deps = descriptor f ^. dependency-      imports = Set.toAscList $ Set.fromList-                  [ haskellModule (filesByName ! exportName)-                  | dep <- deps-                  , exportName <- exports (filesByName ! dep)-                  ]-      in generateModule (haskellModule f) imports-             (fileSyntaxType (descriptor f))-             modifyImports-             (definitions f)-             (collectEnvFromDeps deps filesByName)-  in [ ( outputFilePath . prettyPrint . haskellModule $ f-       , header (descriptor f) <> pack (prettyPrint $ buildFile f)-       )-     | fileName <- toGenerate-     , let f = filesByName ! fileName-     ]-