From 1c45c1940d4a8ec5ddba6c264d0f4678df972af5 Mon Sep 17 00:00:00 2001 From: Judah Jacobson Date: Tue, 22 Oct 2019 22:03:15 -0700 Subject: [PATCH] Reduce the API of proto-lens-protoc. (#357) Previously we exposed most of the internals as a library, with the hope that they'd be useful for plugins (i.e. for services). But now we're exposing service information at the type level, and the library continues to be unused. Removing most of it allows us to change the internals without requiring a major version bump. For example, with this change the migration to ghc-source-gen would have only required a minor version bump. Fixes #347. I think it's not feasible to completely remove the library at this time, since we still want to share logic around module names between proto-lens-protoc and proto-lens-setup. --- Changelog.md | 3 ++ .../Data/ProtoLens/Compiler/Definitions.hs | 0 .../Data/ProtoLens/Compiler/Generate.hs | 0 .../ProtoLens/Compiler/Generate/Commented.hs | 0 .../ProtoLens/Compiler/Generate/Encoding.hs | 0 .../Data/ProtoLens/Compiler/Generate/Field.hs | 0 .../Data/ProtoLens/Compiler/Plugin.hs | 44 ++++--------------- app/protoc-gen-haskell.hs | 7 +-- package.yaml | 25 +++++------ src/Data/ProtoLens/Compiler/ModuleName.hs | 24 ++++++++++ 10 files changed, 49 insertions(+), 54 deletions(-) rename {src => app}/Data/ProtoLens/Compiler/Definitions.hs (100%) rename {src => app}/Data/ProtoLens/Compiler/Generate.hs (100%) rename {src => app}/Data/ProtoLens/Compiler/Generate/Commented.hs (100%) rename {src => app}/Data/ProtoLens/Compiler/Generate/Encoding.hs (100%) rename {src => app}/Data/ProtoLens/Compiler/Generate/Field.hs (100%) rename {src => app}/Data/ProtoLens/Compiler/Plugin.hs (71%) create mode 100644 src/Data/ProtoLens/Compiler/ModuleName.hs diff --git a/Changelog.md b/Changelog.md index 2030af37..c529d111 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,9 @@ - Use `ghc-source-gen` instead of `haskell-src-exts`. Removes `Data.ProtoLens.Compiler.Combinators` and adds `Data.ProtoLens.Compiler.Definitions`. +- Limit the exposed library to a single module + `Data.ProtoLens.Compiler.ModuleName`. The other modules were not + used in practice, and exposed internals unnecessarily. ### Backwards-Compatible Changes - Fix a potential naming conflict when message types and enum values diff --git a/src/Data/ProtoLens/Compiler/Definitions.hs b/app/Data/ProtoLens/Compiler/Definitions.hs similarity index 100% rename from src/Data/ProtoLens/Compiler/Definitions.hs rename to app/Data/ProtoLens/Compiler/Definitions.hs diff --git a/src/Data/ProtoLens/Compiler/Generate.hs b/app/Data/ProtoLens/Compiler/Generate.hs similarity index 100% rename from src/Data/ProtoLens/Compiler/Generate.hs rename to app/Data/ProtoLens/Compiler/Generate.hs diff --git a/src/Data/ProtoLens/Compiler/Generate/Commented.hs b/app/Data/ProtoLens/Compiler/Generate/Commented.hs similarity index 100% rename from src/Data/ProtoLens/Compiler/Generate/Commented.hs rename to app/Data/ProtoLens/Compiler/Generate/Commented.hs diff --git a/src/Data/ProtoLens/Compiler/Generate/Encoding.hs b/app/Data/ProtoLens/Compiler/Generate/Encoding.hs similarity index 100% rename from src/Data/ProtoLens/Compiler/Generate/Encoding.hs rename to app/Data/ProtoLens/Compiler/Generate/Encoding.hs diff --git a/src/Data/ProtoLens/Compiler/Generate/Field.hs b/app/Data/ProtoLens/Compiler/Generate/Field.hs similarity index 100% rename from src/Data/ProtoLens/Compiler/Generate/Field.hs rename to app/Data/ProtoLens/Compiler/Generate/Field.hs diff --git a/src/Data/ProtoLens/Compiler/Plugin.hs b/app/Data/ProtoLens/Compiler/Plugin.hs similarity index 71% rename from src/Data/ProtoLens/Compiler/Plugin.hs rename to app/Data/ProtoLens/Compiler/Plugin.hs index 51a4f9f2..84eb0a10 100644 --- a/src/Data/ProtoLens/Compiler/Plugin.hs +++ b/app/Data/ProtoLens/Compiler/Plugin.hs @@ -14,26 +14,19 @@ module Data.ProtoLens.Compiler.Plugin , ProtoFile(..) , analyzeProtoFiles , collectEnvFromDeps - , outputFilePath - , moduleName - , moduleNameStr ) where -import Data.Char (toUpper) -import Data.List (foldl', intercalate) +import Data.List (foldl') import qualified Data.Map.Strict as Map import Data.Map.Strict (Map, unions, (!)) -#if !MIN_VERSION_base(4,11,0) -import Data.Semigroup ((<>)) -#endif import Data.String (fromString) import qualified Data.Text as T import Data.Text (Text) import Lens.Family2 import Proto.Google.Protobuf.Descriptor (FileDescriptorProto) -import System.FilePath (dropExtension, splitDirectories) import Data.ProtoLens.Compiler.Definitions +import Data.ProtoLens.Compiler.ModuleName import GHC.SourceGen (ModuleNameStr, OccNameStr, RdrNameStr) @@ -51,12 +44,12 @@ data ProtoFile = ProtoFile -- Given a list of FileDescriptorProtos, collect information about each file -- into a map of 'ProtoFile's keyed by 'ProtoFileName'. -analyzeProtoFiles :: Text -> [FileDescriptorProto] -> Map ProtoFileName ProtoFile -analyzeProtoFiles modulePrefix files = +analyzeProtoFiles :: [FileDescriptorProto] -> Map ProtoFileName ProtoFile +analyzeProtoFiles files = Map.fromList [ (f ^. #name, ingestFile f) | f <- files ] where filesByName = Map.fromList [(f ^. #name, f) | f <- files] - moduleNames = fmap (moduleName modulePrefix) filesByName + moduleNames = fmap fdModuleName filesByName -- The definitions in each input proto file, indexed by filename. definitionsByName = fmap collectDefinitions filesByName servicesByName = fmap collectServices filesByName @@ -83,31 +76,10 @@ collectEnvFromDeps :: [ProtoFileName] -> Map ProtoFileName ProtoFile -> Env RdrN collectEnvFromDeps deps filesByName = unions $ fmap (exportedEnv . (filesByName !)) deps --- | Get the output file path (for CodeGeneratorResponse.File) for a Haskell --- ModuleName. -outputFilePath :: String -> Text -outputFilePath n = T.replace "." "/" (T.pack n) <> ".hs" - -- | Get the Haskell 'ModuleName' corresponding to a given .proto file. -moduleName :: Text -> FileDescriptorProto -> ModuleNameStr -moduleName modulePrefix fd - = fromString $ moduleNameStr (T.unpack modulePrefix) (T.unpack $ fd ^. #name) - --- | Get the Haskell module name corresponding to a given .proto file. -moduleNameStr :: String -> FilePath -> String -moduleNameStr prefix path = fixModuleName rawModuleName - where - 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 "." - . (prefix :) - . splitDirectories $ dropExtension - $ path - +fdModuleName :: FileDescriptorProto -> ModuleNameStr +fdModuleName fd + = fromString $ protoModuleName (T.unpack $ fd ^. #name) -- | Given a list of .proto files (topologically sorted), determine which -- files' definitions are exported by which files. diff --git a/app/protoc-gen-haskell.hs b/app/protoc-gen-haskell.hs index 23edda97..13abbaf2 100644 --- a/app/protoc-gen-haskell.hs +++ b/app/protoc-gen-haskell.hs @@ -78,8 +78,7 @@ makeResponse dflags prog request = let generateFiles :: DynFlags -> ModifyImports -> (FileDescriptorProto -> Text) -> [FileDescriptorProto] -> [ProtoFileName] -> [(Text, Text)] generateFiles dflags modifyImports header files toGenerate = let - modulePrefix = "Proto" - filesByName = analyzeProtoFiles modulePrefix files + filesByName = analyzeProtoFiles files -- The contents of the generated Haskell file for a given .proto file. modulesToBuild :: ProtoFile -> [CommentedModule] modulesToBuild f = let @@ -92,7 +91,7 @@ generateFiles dflags modifyImports header files toGenerate = let (definitions f) (collectEnvFromDeps deps filesByName) (services f) - in [ ( outputFilePath $ showPpr dflags $ getModuleName modul + in [ ( moduleFilePath $ pack $ showPpr dflags (getModuleName modul) , header (descriptor f) <> pack (showPpr dflags modul) ) | fileName <- toGenerate @@ -100,3 +99,5 @@ generateFiles dflags modifyImports header files toGenerate = let , modul <- modulesToBuild f ] +moduleFilePath :: Text -> Text +moduleFilePath n = T.replace "." "/" n <> ".hs" diff --git a/package.yaml b/package.yaml index 1068e251..d477fb27 100644 --- a/package.yaml +++ b/package.yaml @@ -18,30 +18,25 @@ extra-source-files: dependencies: - base >= 4.9 && < 4.14 - - containers >= 0.5 && < 0.7 - - lens-family >= 1.2 && < 2.1 - - proto-lens == 0.5.* - - text == 1.2.* - - ghc-source-gen == 0.3.* - - ghc >= 8.2 && < 8.10 - + - filepath >= 1.4 && < 1.6 library: source-dirs: src - dependencies: - - filepath >= 1.4 && < 1.6 - - pretty == 1.1.* exposed-modules: - - Data.ProtoLens.Compiler.Definitions - - Data.ProtoLens.Compiler.Generate - - Data.ProtoLens.Compiler.Generate.Commented - - Data.ProtoLens.Compiler.Plugin + - Data.ProtoLens.Compiler.ModuleName executables: proto-lens-protoc: main: protoc-gen-haskell.hs source-dirs: app dependencies: - - ghc-paths == 0.1.* - bytestring == 0.10.* + - containers >= 0.5 && < 0.7 + - ghc >= 8.2 && < 8.10 + - ghc-paths == 0.1.* + - ghc-source-gen == 0.3.* + - lens-family >= 1.2 && < 2.1 + - pretty == 1.1.* + - proto-lens == 0.5.* - proto-lens-protoc + - text == 1.2.* diff --git a/src/Data/ProtoLens/Compiler/ModuleName.hs b/src/Data/ProtoLens/Compiler/ModuleName.hs new file mode 100644 index 00000000..c31823b0 --- /dev/null +++ b/src/Data/ProtoLens/Compiler/ModuleName.hs @@ -0,0 +1,24 @@ +module Data.ProtoLens.Compiler.ModuleName + ( protoModuleName ) where + +import Data.Char (toUpper) +import Data.List (intercalate) +import System.FilePath + +-- | Get the Haskell module name corresponding to a given .proto file. +protoModuleName :: FilePath -> String +protoModuleName path = fixModuleName rawModuleName + where + 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 "." + . (prefix :) + . splitDirectories $ dropExtension + $ path + +prefix :: String +prefix = "Proto"