Skip to content

Commit

Permalink
Use per-edition feature defaults instead of syntax type to control be…
Browse files Browse the repository at this point in the history
…havior.
  • Loading branch information
chungyc committed Oct 1, 2024
1 parent 1818e75 commit 76e4619
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 41 deletions.
106 changes: 66 additions & 40 deletions proto-lens-protoc/app/Data/ProtoLens/Compiler/Definitions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import Data.Maybe (catMaybes, fromMaybe)
import Data.Monoid ((<>))
#endif
import Data.ProtoLens.Labels ()
import Data.ProtoLens.Compiler.Editions.Features (featuresForEdition)
import qualified Data.Semigroup as Semigroup
import qualified Data.Set as Set
import Data.String (IsString(..))
Expand All @@ -64,8 +65,13 @@ import Data.Tree
import Lens.Family2 ((^.), (^..), toListOf)
import Proto.Google.Protobuf.Descriptor
( DescriptorProto
, Edition(..)
, EnumDescriptorProto
, EnumValueDescriptorProto
, FeatureSet
, FeatureSet'FieldPresence(..)
, FeatureSet'EnumType(..)
, FeatureSet'RepeatedFieldEncoding(..)
, FieldDescriptorProto
, FieldDescriptorProto'Label(..)
, FieldDescriptorProto'Type(..)
Expand Down Expand Up @@ -93,16 +99,29 @@ import GHC.SourceGen
-- either from this or another file).
type Env n = Map.Map Text (Definition n)

data SyntaxType = Proto2 | Proto3 | Editions
deriving (Show, Eq)
{-| Returns the edition for the file.
fileSyntaxType :: FileDescriptorProto -> SyntaxType
fileSyntaxType f = case f ^. #syntax of
"proto2" -> Proto2
"proto3" -> Proto3
"editions" -> Editions
"" -> Proto2 -- The proto compiler doesn't set syntax for proto2 files.
s -> error $ "Unknown syntax type " ++ show s
For proto2 and proto3 files, 'EDITION_PROTO2' and 'EDITION_PROTO3' are returned,
respectively, which will map to the equivalent feature set compatible with
proto2 and proto3.
>>> fileEdition $ defMessage & #syntax .~ "proto2"
EDITION_PROTO2
>>> fileEdition $ defMessage & #syntax .~ "proto3"
EDITION_PROTO3
-}
fileEdition :: FileDescriptorProto -> Edition
fileEdition f = case f ^. #syntax of
"editions" -> f ^. #edition
"proto3" -> EDITION_PROTO3
"proto2" -> EDITION_PROTO2
"" -> EDITION_PROTO2
s -> error $ "Unknown syntax type " ++ show s

{-| Returns the feature defaults for the file. -}
fileFeatures :: FileDescriptorProto -> FeatureSet
fileFeatures f = featuresForEdition $ fileEdition f

data Definition n = Message (MessageInfo n) | Enum (EnumInfo n)
deriving Functor
Expand Down Expand Up @@ -309,7 +328,7 @@ collectDefinitions fd = let
p -> "." <> p <> "."
hsPrefix = ""
in Map.fromList $ concatMap flatten $
messageAndEnumDefs (fileSyntaxType fd)
messageAndEnumDefs (fileFeatures fd)
protoPrefix hsPrefix Map.empty
(fd ^. #messageType) (fd ^. #enumType)

Expand Down Expand Up @@ -337,25 +356,25 @@ collectServices fd = fmap (toServiceInfo $ fd ^. #package) $ fd ^. #service
}

messageAndEnumDefs ::
SyntaxType -> Text -> String
FeatureSet -> Text -> String
-> GroupMap
-- ^ Group fields of the parent message (if any).
-> [DescriptorProto]
-> [EnumDescriptorProto]
-> Forest (Text, Definition OccNameStr)
-- ^ Organized as a list of trees, to make it possible for callers
-- to get the immediate child nested types.
messageAndEnumDefs syntaxType protoPrefix hsPrefix groups messages enums
= map (messageDefs syntaxType protoPrefix hsPrefix groups) messages
messageAndEnumDefs features protoPrefix hsPrefix groups messages enums
= map (messageDefs features protoPrefix hsPrefix groups) messages
++ map
(flip Node [] -- Enums have no sub-definitions
. enumDef syntaxType protoPrefix hsPrefix)
. enumDef features protoPrefix hsPrefix)
enums

-- | Generate the definitions for a message and its nested types (if any).
messageDefs :: SyntaxType -> Text -> String -> GroupMap -> DescriptorProto
messageDefs :: FeatureSet -> Text -> String -> GroupMap -> DescriptorProto
-> Tree (Text, Definition OccNameStr)
messageDefs syntaxType protoPrefix hsPrefix groups d
messageDefs features protoPrefix hsPrefix groups d
= Node (protoName, thisDef) subDefs
where
protoName = protoPrefix <> d ^. #name
Expand All @@ -371,15 +390,15 @@ messageDefs syntaxType protoPrefix hsPrefix groups d
, messageDescriptor = d
, messageFields =
map (PlainFieldInfo <$>
(fieldKind syntaxType mapEntries) <*> (fieldInfo hsPrefix'))
(fieldKind features mapEntries) <*> (fieldInfo hsPrefix'))
$ Map.findWithDefault [] Nothing allFields
, messageOneofFields = collectOneofFields hsPrefix' d allFields
, messageUnknownFields =
fromString $ "_" ++ hsPrefix' ++ "_unknownFields"
, groupFieldNumber = Map.lookup protoName groups
}
subDefs = messageAndEnumDefs
syntaxType
features
(protoName <> ".")
hsPrefix'
(collectGroupFields $ d ^. #field)
Expand Down Expand Up @@ -427,15 +446,19 @@ fieldInfo hsPrefix f = FieldInfo
}

fieldKind ::
SyntaxType -> Map.Map Text MapEntryInfo -> FieldDescriptorProto
FeatureSet -> Map.Map Text MapEntryInfo -> FieldDescriptorProto
-> FieldKind
fieldKind syntaxType mapEntries f = case f ^. #label of
FieldDescriptorProto'LABEL_OPTIONAL
| syntaxType == Proto3
&& f ^. #type' /= FieldDescriptorProto'TYPE_MESSAGE
&& not (f ^. #proto3Optional)
-> OptionalValueField
| otherwise -> OptionalMaybeField
fieldKind features mapEntries f = case f ^. #label of
FieldDescriptorProto'LABEL_OPTIONAL ->
case features ^. #fieldPresence of
FeatureSet'IMPLICIT
| f ^. #type' /= FieldDescriptorProto'TYPE_MESSAGE
&& not (f ^. #proto3Optional)
-> OptionalValueField
| otherwise -> OptionalMaybeField
FeatureSet'EXPLICIT -> OptionalMaybeField
FeatureSet'LEGACY_REQUIRED -> RequiredField
_ -> error $ "Has unknown field presence: " ++ show (f ^. #name)
FieldDescriptorProto'LABEL_REQUIRED -> RequiredField
FieldDescriptorProto'LABEL_REPEATED
| Just entryInfo <- Map.lookup (f ^. #typeName) mapEntries
Expand All @@ -446,10 +469,11 @@ fieldKind syntaxType mapEntries f = case f ^. #label of
| f ^. #type' `elem` unpackableTypes = NotPackable
| packedByDefault = Packed
| otherwise = Packable
-- If the "packed" attribute isn't set, then default to packed if proto3.
-- Unfortunately, protoc doesn't implement this logic for us automatically.
packedByDefault = fromMaybe (syntaxType == Proto3)
$ f ^. #options . #maybe'packed

packedByDefault =
fromMaybe (features ^. #repeatedFieldEncoding == FeatureSet'PACKED)
$ f ^. #options . #maybe'packed

unpackableTypes =
[ FieldDescriptorProto'TYPE_MESSAGE
, FieldDescriptorProto'TYPE_GROUP
Expand Down Expand Up @@ -603,24 +627,26 @@ reservedKeywords = Set.fromList $
]

-- | Generate the definition for an enum type.
enumDef :: SyntaxType -> Text -> String -> EnumDescriptorProto
enumDef :: FeatureSet -> Text -> String -> EnumDescriptorProto
-> (Text, Definition OccNameStr)
enumDef syntaxType protoPrefix hsPrefix d = let
enumDef features protoPrefix hsPrefix d = let
mkText n = protoPrefix <> n
mkHsName n = fromString $ hsPrefix ++ case hsName n of
('_':xs) -> 'X':xs
xs -> xs
in (mkText (d ^. #name)
, Enum EnumInfo
{ enumName = mkHsName (d ^. #name)
, enumUnrecognized = if syntaxType == Proto2
then Nothing
else Just EnumUnrecognizedInfo
{ unrecognizedName
= mkHsName (d ^. #name <> "'Unrecognized")
, unrecognizedValueName
= mkHsName (d ^. #name <> "'UnrecognizedValue")
}
, enumUnrecognized = case features ^. #enumType of
FeatureSet'CLOSED -> Nothing
FeatureSet'OPEN ->
Just EnumUnrecognizedInfo
{ unrecognizedName
= mkHsName (d ^. #name <> "'Unrecognized")
, unrecognizedValueName
= mkHsName (d ^. #name <> "'UnrecognizedValue")
}
_ -> error $ "Has unknown enum type: " ++ show (d ^. #name)
, enumDescriptor = d
, enumValues = collectEnumValues mkHsName $ d ^. #value
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ for a particular edition.
featuresForEditionFromDefaults :: FeatureSetDefaults -> Edition -> FeatureSet
featuresForEditionFromDefaults defaults edition
| (d : _) <- candidates = (d ^. #overridableFeatures) `mergedInto` (d ^. #fixedFeatures)
| otherwise = defMessage
| otherwise = error $ "Unsupported edition with tag number: " ++ show (fromEnum edition)
where
candidates = dropWhile (\d -> d ^. #edition > edition) recentFirst

Expand Down

0 comments on commit 76e4619

Please sign in to comment.