-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proto3 enums #137
Proto3 enums #137
Changes from 5 commits
8605326
176a5d5
bccef23
1d716f0
da6d4db
9d1a76d
eb4f5e4
2d0974b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ import Control.Arrow (second) | |
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 | ||
|
@@ -53,7 +53,7 @@ import Data.ProtoLens.Compiler.Combinators | |
import Data.ProtoLens.Compiler.Definitions | ||
|
||
data SyntaxType = Proto2 | Proto3 | ||
deriving Eq | ||
deriving (Show, Eq) | ||
|
||
fileSyntaxType :: FileDescriptorProto -> SyntaxType | ||
fileSyntaxType f = case f ^. syntax of | ||
|
@@ -78,10 +78,12 @@ generateModule :: ModuleName | |
-> [Module] | ||
generateModule modName imports syntaxType modifyImport definitions importedEnv | ||
= [ module' modName | ||
(Just $ concatMap generateExports $ Map.elems definitions) | ||
pragmas | ||
sharedImports | ||
. concatMap generateDecls $ Map.toList definitions | ||
, module' fieldModName | ||
Nothing | ||
pragmas | ||
sharedImports | ||
(concatMap generateFieldDecls allLensNames) | ||
|
@@ -91,12 +93,15 @@ generateModule modName imports syntaxType modifyImport definitions importedEnv | |
pragmas = | ||
[ languagePragma $ map fromString | ||
["ScopedTypeVariables", "DataKinds", "TypeFamilies", | ||
"UndecidableInstances", | ||
"UndecidableInstances", "GeneralizedNewtypeDeriving", | ||
"MultiParamTypeClasses", "FlexibleContexts", "FlexibleInstances", | ||
"PatternSynonyms", "MagicHash", "NoImplicitPrelude"] | ||
-- 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" | ||
] | ||
sharedImports = map (modifyImport . importSimple) | ||
[ "Prelude", "Data.Int", "Data.Word" | ||
|
@@ -109,7 +114,9 @@ generateModule modName imports syntaxType modifyImport definitions importedEnv | |
env = Map.union (unqualifyEnv definitions) importedEnv | ||
generateDecls (protoName, Message m) | ||
= generateMessageDecls syntaxType env (stripDotPrefix protoName) m | ||
generateDecls (_, Enum e) = generateEnumDecls e | ||
generateDecls (_, Enum e) = generateEnumDecls syntaxType e | ||
generateExports (Message m) = generateMessageExports m | ||
generateExports (Enum e) = generateEnumExports syntaxType e | ||
allLensNames = F.toList $ Set.fromList | ||
[ lensSymbol inst | ||
| Message m <- Map.elems definitions | ||
|
@@ -149,6 +156,11 @@ reexported imp@ImportDecl {importModule = m} | |
where | ||
m' = fromString $ "Data.ProtoLens.Reexport." ++ prettyPrint m | ||
|
||
generateMessageExports :: MessageInfo Name -> [ExportSpec] | ||
generateMessageExports m = | ||
map (exportAll . unQual) | ||
$ messageName m : map oneofTypeName (messageOneofFields m) | ||
|
||
generateMessageDecls :: SyntaxType -> Env QName -> T.Text -> MessageInfo Name -> [Decl] | ||
generateMessageDecls syntaxType env protoName info = | ||
-- data Bar = Bar { | ||
|
@@ -161,7 +173,7 @@ generateMessageDecls syntaxType env protoName info = | |
] | ||
++ [(messageUnknownFields info, "Data.ProtoLens.FieldSet")] | ||
] | ||
["Prelude.Show", "Prelude.Eq", "Prelude.Ord"] | ||
$ deriving' ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"] | ||
] ++ | ||
|
||
-- oneof field data type declarations | ||
|
@@ -179,7 +191,7 @@ generateMessageDecls syntaxType env protoName info = | |
, let f = caseField c | ||
, let consName = caseConstructorName c | ||
] | ||
["Prelude.Show", "Prelude.Eq", "Prelude.Ord"] | ||
$ deriving' ["Prelude.Show", "Prelude.Eq", "Prelude.Ord"] | ||
| oneofInfo <- messageOneofFields info | ||
] ++ | ||
-- instance (HasLens' f Foo x a, HasLens' f Foo x b, a ~ b) | ||
|
@@ -236,11 +248,94 @@ generateMessageDecls syntaxType env protoName info = | |
dataName = messageName info | ||
allFields = allMessageFields syntaxType env info | ||
|
||
generateEnumDecls :: EnumInfo Name -> [Decl] | ||
generateEnumDecls info = | ||
generateEnumExports :: SyntaxType -> EnumInfo Name -> [ExportSpec] | ||
generateEnumExports syntaxType e = [exportAll n, exportWith n aliases] | ||
where | ||
n = unQual $ enumName e | ||
aliases = [enumValueName v | v <- enumValues e, needsManualExport v] | ||
needsManualExport v = syntaxType == Proto3 | ||
|| isJust (enumAliasOf v) | ||
|
||
generateEnumDecls :: SyntaxType -> EnumInfo Name -> [Decl] | ||
generateEnumDecls Proto3 info = | ||
-- newtype Foo = Foo Int32 | ||
[ newtypeDecl dataName "Data.Int.Int32" | ||
$ deriving' ["Prelude.Eq", "Prelude.Ord", "Prelude.Enum", "Prelude.Bounded"] | ||
|
||
-- instance Show Foo where | ||
-- showsPrec _ Value0 = "Value0" -- the Haskell name | ||
-- showsPrec p (Foo k) = showParen (p > 10) | ||
-- $ showString "toEnum " . shows k | ||
, instDecl [] ("Prelude.Show" `ihApp` [dataType]) | ||
[ [ match "showsPrec" [pWildCard, pApp (unQual n) []] | ||
$ "Prelude.showString" @@ stringExp (prettyPrint n) | ||
| n <- map enumValueName $ enumValues info | ||
] | ||
++ | ||
[ match "showsPrec" ["p", pApp (unQual dataName) ["k"]] | ||
$ "Prelude.showParen" @@ ("Prelude.>" @@ "p" @@ litInt 10) | ||
@@ ("Prelude.." @@ ("Prelude.showString" | ||
@@ stringExp "toEnum ") | ||
@@ ("Prelude.shows" @@ "k")) | ||
] | ||
] | ||
|
||
-- instance MessageEnum Foo where | ||
-- maybeToEnum k = Just $ toEnum k | ||
-- showEnum (Foo 0) = "Value0" -- the proto name | ||
-- showEnum (Foo k) = show k | ||
-- readEnum "Value0" = Just (Foo 0) | ||
-- readEnum _ = Nothing | ||
, instDecl [] ("Data.ProtoLens.MessageEnum" `ihApp` [dataType]) | ||
[ [match "maybeToEnum" ["k"] | ||
$ "Prelude.Just" @@ ("Prelude.toEnum" @@ "k")] | ||
, [ match "showEnum" [pVar n] $ stringExp pn | ||
| v <- enumValues info | ||
, isNothing (enumAliasOf v) | ||
, let n = enumValueName v | ||
, let pn = T.unpack $ enumValueDescriptor v ^. name | ||
] | ||
, [ match "showEnum" [pApp (unQual dataName) ["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" [pWildCard] "Prelude.Nothing" | ||
] | ||
] | ||
|
||
-- proto3 enums always default to zero. | ||
, instDecl [] ("Data.Default.Class.Default" `ihApp` [dataType]) | ||
[[match "def" [] $ "Prelude.toEnum" @@ litInt 0]] | ||
, instDecl [] ("Data.ProtoLens.FieldDefault" `ihApp` [dataType]) | ||
[[match "fieldDefault" [] $ "Prelude.toEnum" @@ litInt 0]] | ||
] | ||
++ | ||
-- pattern Value0 :: Foo | ||
-- pattern Value0 = Foo 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. <3 |
||
concat | ||
[ [ patSynSig n dataType | ||
, patSyn (pVar n) | ||
$ pApp (unQual dataName) [pLitInt k] | ||
] | ||
| v <- enumValues info | ||
, let n = enumValueName v | ||
, let k = fromIntegral $ enumValueDescriptor v ^. number | ||
] | ||
where | ||
dataName = enumName info | ||
dataType = tyCon $ unQual dataName | ||
|
||
generateEnumDecls Proto2 info = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what happens to dependent haskell code if you change the proto version? would anyone ever do that? if so, do these separate representations cause any problems? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you change from proto2 to proto3 you could incomplete patterns, e.g. in
when the enum becomes an open type. But |
||
[ 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]) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the reason for this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was part of #136 (now merged). Sorry for the noise.
Previously the modules that we generated always exported everything via
module M where
, so the export list parameter was alwaysNothing
. Now, for the types module we need to pass an explicit list, i.e.,module M (...) where
instead, so it can export pattern synonyms in a nicer way.