Skip to content

Commit 9a89cc0

Browse files
tim2CF21itEvan Relf
authored
proto3 standard compatibility improvements (#143)
* expose Proto3.Suite.DotProto.Internal * cabal fix * conditional compilation according Swagger version * globalIdentifier support, circleci support * DotProtoMessageOption * data PartRank for easier ordering, refactored CanonicalRank implementation of DotProtoMessagePart, removed CircleCI support * optional swagger2isAbove_2_4_0 flag for getting correct proto3-suite-linux generator binary matching local swagger2 version * better swagger2 overrides * WIP debug namespace * qualifiedMessageTypeName function to fix deep nested message namespace codegen bug * Ignore `result-*` files Co-authored-by: Ilja Tkachuk <tkachuk.labs@gmail.com> Co-authored-by: Evan Relf <evan@awakesecurity.com>
1 parent e57fb0b commit 9a89cc0

19 files changed

+1316
-23
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ dist/
77
dist-newstyle/
88
.stack-work/
99
result
10+
result-*
1011
*.pyc
1112
# This should be auto-generated by protoc
1213
test-files/test_proto_pb2.py

proto3-suite.cabal

+3
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ test-suite tests
119119
TestProtoImport
120120
TestProtoOneof
121121
TestProtoOneofImport
122+
--TestProtoLeadingDot
123+
TestProtoNestedMessage
124+
--TestProtoProtocPlugin
122125

123126
hs-source-dirs: tests gen
124127
default-language: Haskell2010

src/Proto3/Suite/DotProto/AST.hs

+1
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ data DotProtoMessagePart
342342
| DotProtoMessageOneOf DotProtoIdentifier [DotProtoField]
343343
| DotProtoMessageDefinition DotProtoDefinition
344344
| DotProtoMessageReserved [DotProtoReservedField]
345+
| DotProtoMessageOption DotProtoOption
345346
deriving (Show, Eq)
346347

347348
instance Arbitrary DotProtoMessagePart where

src/Proto3/Suite/DotProto/Generate.hs

+7-7
Original file line numberDiff line numberDiff line change
@@ -314,10 +314,10 @@ ctxtImports = fmap (map mkImport . nub)
314314
-- been provided with a valid module path in its 'dotProtoTypeInfoModulePath'
315315
-- field. The latter describes the name of the Haskell module being generated.
316316
msgTypeFromDpTypeInfo :: MonadError CompileError m
317-
=> DotProtoTypeInfo -> DotProtoIdentifier -> m HsType
318-
msgTypeFromDpTypeInfo DotProtoTypeInfo{..} ident = do
317+
=> TypeContext -> DotProtoTypeInfo -> DotProtoIdentifier -> m HsType
318+
msgTypeFromDpTypeInfo ctxt DotProtoTypeInfo{..} ident = do
319319
modName <- modulePathModName dotProtoTypeInfoModulePath
320-
identName <- qualifiedMessageName dotProtoTypeInfoParent ident
320+
identName <- qualifiedMessageTypeName ctxt dotProtoTypeInfoParent ident
321321
pure $ HsTyCon (Qual modName (HsIdent identName))
322322

323323
haskellName, jsonpbName, grpcName, protobufName, protobufWrapperName, proxyName :: String -> HsQName
@@ -446,8 +446,8 @@ dptToHsTypeWrapped opts =
446446
Named msgName ->
447447
case M.lookup msgName ctxt of
448448
Just ty@(DotProtoTypeInfo { dotProtoTypeInfoKind = DotProtoKindEnum }) ->
449-
HsTyApp (protobufType_ "Enumerated") <$> msgTypeFromDpTypeInfo ty msgName
450-
Just ty -> msgTypeFromDpTypeInfo ty msgName
449+
HsTyApp (protobufType_ "Enumerated") <$> msgTypeFromDpTypeInfo ctxt ty msgName
450+
Just ty -> msgTypeFromDpTypeInfo ctxt ty msgName
451451
Nothing -> noSuchTypeError msgName
452452

453453
foldDPT :: MonadError CompileError m
@@ -541,8 +541,8 @@ dpptToHsType ctxt = \case
541541
Named msgName ->
542542
case M.lookup msgName ctxt of
543543
Just ty@(DotProtoTypeInfo { dotProtoTypeInfoKind = DotProtoKindEnum }) ->
544-
HsTyApp (protobufType_ "Enumerated") <$> msgTypeFromDpTypeInfo ty msgName
545-
Just ty -> msgTypeFromDpTypeInfo ty msgName
544+
HsTyApp (protobufType_ "Enumerated") <$> msgTypeFromDpTypeInfo ctxt ty msgName
545+
Just ty -> msgTypeFromDpTypeInfo ctxt ty msgName
546546
Nothing -> noSuchTypeError msgName
547547

548548
validMapKey :: DotProtoPrimType -> Bool

src/Proto3/Suite/DotProto/Generate/Swagger.hs

+4
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ instance {-# OVERLAPPING #-} (ToJSONKey k, ToSchema k) => ToSchema (OverrideToSc
8383
declareNamedSchema (Proxy :: Proxy [(k, (OverrideToSchema ByteString))])
8484
where
8585
schema_ = mempty
86+
#if MIN_VERSION_swagger2(2,4,0)
8687
& type_ ?~ SwaggerObject
88+
#else
89+
& type_ .~ SwaggerObject
90+
#endif
8791
& additionalProperties ?~ AdditionalPropertiesSchema (Inline byteSchema)
8892

8993
{-| This is a convenience function that uses type inference to select the

src/Proto3/Suite/DotProto/Internal.hs

+22
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,28 @@ nestedTypeName (Qualified {}) _ = internalError "nestedTypeName: Qualifi
437437
qualifiedMessageName :: MonadError CompileError m => DotProtoIdentifier -> DotProtoIdentifier -> m String
438438
qualifiedMessageName parentIdent msgIdent = nestedTypeName parentIdent =<< dpIdentUnqualName msgIdent
439439

440+
qualifiedMessageTypeName :: MonadError CompileError m =>
441+
TypeContext ->
442+
DotProtoIdentifier ->
443+
DotProtoIdentifier ->
444+
m String
445+
qualifiedMessageTypeName ctxt parentIdent msgIdent = do
446+
xs <- parents parentIdent []
447+
case xs of
448+
[] -> nestedTypeName parentIdent =<< dpIdentUnqualName msgIdent
449+
x : xs' -> nestedTypeName (Dots . Path $ x NE.:| xs') =<< dpIdentUnqualName msgIdent
450+
where
451+
parents par@(Single x) xs =
452+
case M.lookup par ctxt of
453+
Just (DotProtoTypeInfo { dotProtoTypeInfoParent = parentIdent' }) ->
454+
parents parentIdent' $ x : xs
455+
Nothing ->
456+
pure $ x : xs
457+
parents Anonymous xs =
458+
pure xs
459+
parents par _ =
460+
internalError $ "qualifiedMessageTypeName: wrong parent " <> show par
461+
440462
--------------------------------------------------------------------------------
441463
--
442464
-- ** Codegen bookkeeping helpers

src/Proto3/Suite/DotProto/Parsing.hs

+7-8
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,10 @@ singleIdentifier = Single <$> token identifierName
103103
identifier :: ProtoParser DotProtoIdentifier
104104
identifier = token _identifier
105105

106-
-- [note] message and enum types are defined by the proto3 spec to have an optional leading period (messageType and enumType in the spec)
107-
-- what this indicates is, as far as i can tell, not documented, and i haven't found this syntax used in practice
108-
-- it's ommitted but can be fairly easily added if there is in fact a use for it
109-
110-
-- [update] the leading dot denotes that the identifier path starts in global scope
111-
-- i still haven't seen a use case for this but i can add it upon request
106+
-- Parses a full identifier, consuming trailing space.
107+
-- The leading dot denotes that the identifier path starts in global scope.
108+
globalIdentifier :: ProtoParser DotProtoIdentifier
109+
globalIdentifier = token $ string "." >> _identifier
112110

113111
-- Parses a nested identifier, consuming trailing space.
114112
nestedIdentifier :: ProtoParser DotProtoIdentifier
@@ -162,7 +160,7 @@ primType = try (symbol "double" $> Double)
162160
<|> try (symbol "string" $> String)
163161
<|> try (symbol "bytes" $> Bytes)
164162
<|> try (symbol "bool" $> Bool)
165-
<|> Named <$> identifier
163+
<|> Named <$> (identifier <|> globalIdentifier)
166164

167165
--------------------------------------------------------------------------------
168166
-- top-level parser and version annotation
@@ -256,7 +254,7 @@ rpcOptions = braces $ many topOption
256254

257255
rpcClause :: ProtoParser (DotProtoIdentifier, Streaming)
258256
rpcClause = do
259-
let sid ctx = (,ctx) <$> identifier
257+
let sid ctx = (,ctx) <$> (identifier <|> globalIdentifier)
260258
-- NB: Distinguish "stream stream.foo" from "stream.foo"
261259
try (symbol "stream" *> sid Streaming) <|> sid NonStreaming
262260

@@ -296,6 +294,7 @@ messagePart = try (DotProtoMessageDefinition <$> enum)
296294
<|> try (DotProtoMessageDefinition <$> message)
297295
<|> try messageOneOf
298296
<|> try (DotProtoMessageField <$> messageField)
297+
<|> try (DotProtoMessageOption <$> topOption)
299298

300299
messageType :: ProtoParser DotProtoType
301300
messageType = try mapType <|> try repType <|> (Prim <$> primType)

src/Proto3/Suite/DotProto/Rendering.hs

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ prettyPrintProtoDefinition opts = defn where
129129
<+> (PP.hcat . PP.punctuate (PP.text ", ") $ pPrint <$> reservations)
130130
<> PP.text ";"
131131
msgPart msgName (DotProtoMessageOneOf name fields) = vbraces (PP.text "oneof" <+> pPrint name) (PP.vcat $ field msgName <$> fields)
132+
msgPart _ (DotProtoMessageOption opt)
133+
= PP.text "option" <+> pPrint opt <> PP.text ";"
132134

133135
field :: DotProtoIdentifier -> DotProtoField -> PP.Doc
134136
field msgName (DotProtoField number mtype name options comments)

test-files/leading_dot/data.proto

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
syntax = "proto3";
2+
3+
package LeadingDot.Rpc.Data;
4+
5+
message Request {
6+
uint32 hello = 1;
7+
}
8+
9+
message Response {
10+
uint32 world = 1;
11+
}

0 commit comments

Comments
 (0)