Keep all annotations from .proto files (#222)

Co-authored-by: Flavio Corpa <flavio.corpa@47deg.com>
This commit is contained in:
Alejandro Serrano 2020-09-04 14:36:02 +02:00 committed by GitHub
parent 2a413c7c48
commit fc423dc387
5 changed files with 69 additions and 46 deletions

View File

@ -26,6 +26,7 @@ need to annotate your schema using 'ProtoBufAnnotation'.
module Mu.Adapter.ProtoBuf (
-- * Custom annotations
ProtoBufAnnotation(..)
, ProtoBufOptionConstant(..)
-- * Conversion using schemas
, IsProtoSchema
, toProtoViaSchema
@ -62,16 +63,23 @@ instance ProtoEnum Bool
data ProtoBufAnnotation
= -- | Numeric field identifier for normal fields
-- and whether it should be packed (only used for lists of number-like values)
ProtoBufId Nat Bool
ProtoBufId Nat [(Symbol, ProtoBufOptionConstant)]
-- | List of identifiers for fields which contain a union
| ProtoBufOneOfIds [Nat]
-- Values for constants
data ProtoBufOptionConstant
= ProtoBufOptionConstantInt Nat
| ProtoBufOptionConstantBool Bool
| ProtoBufOptionConstantObject [(Symbol, ProtoBufOptionConstant)]
| ProtoBufOptionConstantOther Symbol
type family FindProtoBufId (sch :: Schema tn fn) (t :: tn) (f :: fn) where
FindProtoBufId sch t f
= FindProtoBufId' t f (GetFieldAnnotation (AnnotatedSchema ProtoBufAnnotation sch) t f)
type family FindProtoBufId' (t :: tn) (f :: fn) (p :: ProtoBufAnnotation) :: Nat where
FindProtoBufId' t f ('ProtoBufId n b) = n
FindProtoBufId' t f ('ProtoBufId n opts) = n
FindProtoBufId' t f other
= TypeError ('Text "protocol buffers id not available for field "
':<>: 'ShowType t ':<>: 'Text "/" ':<>: 'ShowType f)
@ -81,11 +89,22 @@ type family FindProtoBufPacked (sch :: Schema tn fn) (t :: tn) (f :: fn) where
= FindProtoBufPacked' t f (GetFieldAnnotation (AnnotatedSchema ProtoBufAnnotation sch) t f)
type family FindProtoBufPacked' (t :: tn) (f :: fn) (p :: ProtoBufAnnotation) :: Bool where
FindProtoBufPacked' t f ('ProtoBufId n b) = b
FindProtoBufPacked' t f ('ProtoBufId n opts)
= FindProtoBufPacked'' t f opts
FindProtoBufPacked' t f other
= TypeError ('Text "protocol buffers id not available for field "
':<>: 'ShowType t ':<>: 'Text "/" ':<>: 'ShowType f)
type family FindProtoBufPacked'' (t :: tn) (f :: fn) (opts :: [(Symbol, ProtoBufOptionConstant)]) :: Bool where
FindProtoBufPacked'' t f '[] = 'True -- by default we are packed
FindProtoBufPacked'' t f ( '("packed", 'ProtoBufOptionConstantBool b) ': rest )
= b -- found!
FindProtoBufPacked'' t f ( '("packed", other) ': rest )
= TypeError ('Text "non-boolean value for 'packed' for field "
':<>: 'ShowType t ':<>: 'Text "/" ':<>: 'ShowType f)
FindProtoBufPacked'' t f ( other ': rest)
= FindProtoBufPacked'' t f rest
type family FindProtoBufOneOfIds (sch :: Schema tn fn) (t :: tn) (f :: fn) where
FindProtoBufOneOfIds sch t f
= FindProtoBufOneOfIds' t f (GetFieldAnnotation (AnnotatedSchema ProtoBufAnnotation sch) t f)

View File

@ -80,7 +80,7 @@ pbTypeDeclToType (P.DEnum name _ fields) = do
pbChoiceToType :: P.EnumField -> Q (Type, Type)
pbChoiceToType (P.EnumField nm number _)
= (,) <$> [t|'ChoiceDef $(textToStrLit nm) |]
<*> [t|'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit number) 'True) |]
<*> [t|'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit number) '[]) |]
pbTypeDeclToType (P.DMessage name _ _ fields _) = do
(tys, anns) <- unzip <$> mapM pbMsgFieldToType fields
(,) <$> [t|'DRecord $(textToStrLit name) $(pure $ typesToList tys)|] <*> pure anns
@ -90,22 +90,18 @@ pbTypeDeclToType (P.DMessage name _ _ fields _) = do
-- it's possible to distinguish whether it's missing on wire
-- or should be set to the default, so use Option
-- +info -> https://github.com/higherkindness/mu-haskell/pull/130#issuecomment-596433307
pbMsgFieldToType (P.NormalField P.Single ty@(P.TOther _) nm n opts) = do
reportDefaultWarning opts
pbMsgFieldToType (P.NormalField P.Single ty@(P.TOther _) nm n opts) =
(,) <$> [t| 'FieldDef $(textToStrLit nm) ('TOption $(pbFieldTypeToType ty)) |]
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit n) $(pbOptionsToPacked opts)) |]
pbMsgFieldToType (P.NormalField P.Single ty nm n opts) = do
reportDefaultWarning opts
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit n) $(typesToList <$> mapM pbOption opts)) |]
pbMsgFieldToType (P.NormalField P.Single ty nm n opts) =
(,) <$> [t| 'FieldDef $(textToStrLit nm) $(pbFieldTypeToType ty) |]
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit n) $(pbOptionsToPacked opts)) |]
pbMsgFieldToType (P.NormalField P.Repeated ty nm n opts) = do
reportDefaultWarning opts
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit n) $(typesToList <$> mapM pbOption opts)) |]
pbMsgFieldToType (P.NormalField P.Repeated ty nm n opts) =
(,) <$> [t| 'FieldDef $(textToStrLit nm) ('TList $(pbFieldTypeToType ty)) |]
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit n) $(pbOptionsToPacked opts)) |]
pbMsgFieldToType (P.MapField k v nm n opts) = do
reportDefaultWarning opts
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit n) $(typesToList <$> mapM pbOption opts)) |]
pbMsgFieldToType (P.MapField k v nm n opts) =
(,) <$> [t| 'FieldDef $(textToStrLit nm) ('TMap $(pbFieldTypeToType k) $(pbFieldTypeToType v)) |]
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit n) $(pbOptionsToPacked opts)) |]
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm) ('ProtoBufId $(intToLit n) $(typesToList <$> mapM pbOption opts)) |]
pbMsgFieldToType (P.OneOfField nm vs)
| not (all hasFieldNumber vs)
= fail "nested oneof fields are not supported"
@ -114,11 +110,6 @@ pbTypeDeclToType (P.DMessage name _ _ fields _) = do
<*> [t| 'AnnField $(textToStrLit name) $(textToStrLit nm)
('ProtoBufOneOfIds $(typesToList <$> mapM (intToLit . getFieldNumber) vs )) |]
reportDefaultWarning :: [P.Option] -> Q ()
reportDefaultWarning opts =
when (any (\(P.Option ident _) -> ident == ["default"]) opts)
(reportError "mu-protobuf does not (yet) support default values")
pbFieldTypeToType :: P.FieldType -> Q Type
pbFieldTypeToType P.TInt32 = [t|'TPrimitive Int32|]
pbFieldTypeToType P.TUInt32 = fail "unsigned integers are not currently supported"
@ -152,14 +143,27 @@ pbTypeDeclToType (P.DMessage name _ _ fields _) = do
= [t| 'TMap $(pbFieldTypeToType k) $(pbFieldTypeToType v) |]
pbOneOfFieldToType _ = error "this should never happen"
pbOptionsToPacked []
= [t| 'True |]
pbOptionsToPacked (P.Option ["packed"] val : _)
| P.KBool True <- val = [t| 'True |]
| P.KBool False <- val = [t| 'False |]
| otherwise = fail "'packed' with a non-boolean value"
pbOptionsToPacked (_ : rest)
= pbOptionsToPacked rest
pbOption (P.Option oname val)
= do when (oname == ["default"])
(reportError "mu-protobuf does not (yet) support default values")
[t| '( $(textToStrLit (T.intercalate "." oname))
, $(pbConstantToOption val) ) |]
pbConstantToOption (P.KIdentifier names)
= [t| 'ProtoBufOptionConstantOther $(textToStrLit (T.intercalate "." names)) |]
pbConstantToOption (P.KInt n)
= [t| 'ProtoBufOptionConstantInt $(intToLit (fromInteger n)) |]
pbConstantToOption (P.KBool True)
= [t| 'ProtoBufOptionConstantBool 'True |]
pbConstantToOption (P.KBool False)
= [t| 'ProtoBufOptionConstantBool 'False |]
pbConstantToOption (P.KString s)
= [t| 'ProtoBufOptionConstantOther $(textToStrLit s) |]
pbConstantToOption (P.KFloat s)
= [t| 'ProtoBufOptionConstantOther $(textToStrLit (T.pack (show s))) |]
pbConstantToOption (P.KObject s)
= [t| 'ProtoBufOptionConstantObject
$(typesToList <$> mapM (\(n, o) -> [t| '( $(textToStrLit n), $(pbConstantToOption o) ) |] ) s ) |]
typesToList :: [Type] -> Type
typesToList = foldr (AppT . AppT PromotedConsT) PromotedNilT

View File

@ -39,17 +39,17 @@ data MAddress
deriving (FromSchema ExampleSchema "address")
type instance AnnotatedSchema ProtoBufAnnotation ExampleSchema
= '[ 'AnnField "gender" "male" ('ProtoBufId 1 'True)
, 'AnnField "gender" "female" ('ProtoBufId 2 'True)
, 'AnnField "gender" "nb" ('ProtoBufId 3 'True)
, 'AnnField "address" "postcode" ('ProtoBufId 1 'True)
, 'AnnField "address" "country" ('ProtoBufId 2 'True)
, 'AnnField "person" "firstName" ('ProtoBufId 1 'True)
, 'AnnField "person" "lastName" ('ProtoBufId 2 'True)
, 'AnnField "person" "age" ('ProtoBufId 3 'True)
, 'AnnField "person" "gender" ('ProtoBufId 4 'True)
, 'AnnField "person" "address" ('ProtoBufId 5 'True)
, 'AnnField "person" "lucky_numbers" ('ProtoBufId 6 'True) ]
= '[ 'AnnField "gender" "male" ('ProtoBufId 1 '[])
, 'AnnField "gender" "female" ('ProtoBufId 2 '[])
, 'AnnField "gender" "nb" ('ProtoBufId 3 '[])
, 'AnnField "address" "postcode" ('ProtoBufId 1 '[])
, 'AnnField "address" "country" ('ProtoBufId 2 '[])
, 'AnnField "person" "firstName" ('ProtoBufId 1 '[])
, 'AnnField "person" "lastName" ('ProtoBufId 2 '[])
, 'AnnField "person" "age" ('ProtoBufId 3 '[])
, 'AnnField "person" "gender" ('ProtoBufId 4 '[])
, 'AnnField "person" "address" ('ProtoBufId 5 '[])
, 'AnnField "person" "lucky_numbers" ('ProtoBufId 6 '[ '("packed", 'ProtoBufOptionConstantBool 'True) ]) ]
exampleAddress :: MAddress
exampleAddress = MAddress "1111BB" "Spain"

View File

@ -20,9 +20,9 @@ import Mu.Rpc.Examples
import Mu.Schema
type instance AnnotatedSchema ProtoBufAnnotation QuickstartSchema
= '[ 'AnnField "HelloRequest" "name" ('ProtoBufId 1 'True)
, 'AnnField "HelloResponse" "message" ('ProtoBufId 1 'True)
, 'AnnField "HiRequest" "number" ('ProtoBufId 1 'True) ]
= '[ 'AnnField "HelloRequest" "name" ('ProtoBufId 1 '[])
, 'AnnField "HelloResponse" "message" ('ProtoBufId 1 '[])
, 'AnnField "HiRequest" "number" ('ProtoBufId 1 '[]) ]
sayHello' :: HostName -> PortNumber -> T.Text -> IO (GRpcReply T.Text)
sayHello' host port req

View File

@ -9,9 +9,9 @@ import Mu.Rpc.Examples
import Mu.Schema
type instance AnnotatedSchema ProtoBufAnnotation QuickstartSchema
= '[ 'AnnField "HelloRequest" "name" ('ProtoBufId 1 'True)
, 'AnnField "HelloResponse" "message" ('ProtoBufId 1 'True)
, 'AnnField "HiRequest" "number" ('ProtoBufId 1 'True) ]
= '[ 'AnnField "HelloRequest" "name" ('ProtoBufId 1 '[])
, 'AnnField "HelloResponse" "message" ('ProtoBufId 1 '[])
, 'AnnField "HiRequest" "number" ('ProtoBufId 1 '[]) ]
main :: IO ()
main = do