Skip to content

Commit

Permalink
Implement feature resolution for Protobuf Editions.
Browse files Browse the repository at this point in the history
  • Loading branch information
chungyc committed Oct 1, 2024
1 parent bcf5801 commit 0298168
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Description: Exports defaults for native features in Protobuf Editions.
Copyright: Copyright (c) 2024 Google LLC
License: BSD3
-}
module Data.ProtoLens.Compiler.Editions.Defaults (defaults) where
module Data.ProtoLens.Compiler.Editions.Defaults (nativeDefaults) where

import Data.ByteString (ByteString)
import Data.ProtoLens (decodeMessage)
Expand All @@ -23,10 +23,11 @@ import Proto.Google.Protobuf.Descriptor (FeatureSetDefaults)
This contains the defaults from editions @LEGACY@ to @EDITION_2023@
for the native features defined by @google.protobuf.FeatureSet@.
-}
defaults :: FeatureSetDefaults
defaults | Right m <- msg = m
| Left e <- msg = error $ "unable to decode built-in defaults: " ++ e
where msg = decodeMessage serializedDefaults
nativeDefaults :: FeatureSetDefaults
nativeDefaults
| Right m <- msg = m
| Left e <- msg = error $ "unable to decode built-in defaults: " ++ e
where msg = decodeMessage serializedNativeDefaults

{-| Serialized 'FeatureSetDefaults' message containing feature defaults.
Expand All @@ -40,5 +41,5 @@ The message was generated with @protoc@ and translated into a Haskell string:
> ghci> B.readFile "defaults.binpb" >>= print . show
-}
serializedDefaults :: ByteString
serializedDefaults = read "\"\\n\\DC3\\CAN\\132\\a\\\"\\NUL*\\f\\b\\SOH\\DLE\\STX\\CAN\\STX \\ETX(\\SOH0\\STX\\n\\DC3\\CAN\\231\\a\\\"\\NUL*\\f\\b\\STX\\DLE\\SOH\\CAN\\SOH \\STX(\\SOH0\\SOH\\n\\DC3\\CAN\\232\\a\\\"\\f\\b\\SOH\\DLE\\SOH\\CAN\\SOH \\STX(\\SOH0\\SOH*\\NUL \\132\\a(\\232\\a\""
serializedNativeDefaults :: ByteString
serializedNativeDefaults = read "\"\\n\\DC3\\CAN\\132\\a\\\"\\NUL*\\f\\b\\SOH\\DLE\\STX\\CAN\\STX \\ETX(\\SOH0\\STX\\n\\DC3\\CAN\\231\\a\\\"\\NUL*\\f\\b\\STX\\DLE\\SOH\\CAN\\SOH \\STX(\\SOH0\\SOH\\n\\DC3\\CAN\\232\\a\\\"\\f\\b\\SOH\\DLE\\SOH\\CAN\\SOH \\STX(\\SOH0\\SOH*\\NUL \\132\\a(\\232\\a\""
86 changes: 86 additions & 0 deletions proto-lens-protoc/app/Data/ProtoLens/Compiler/Editions/Features.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
-- Copyright 2024 Google LLC. All Rights Reserved.
--
-- Use of this source code is governed by a BSD-style
-- license that can be found in the LICENSE file or at
-- https://developers.google.com/open-source/licenses/bsd

{-# LANGUAGE OverloadedLabels #-}

{-|
Module: Data.ProtoLens.Compiler.Editions.Features
Description: Resolves a feature set for a particular edition with Protobuf Editions.
Copyright: Copyright (c) 2024 Google LLC
License: BSD3
-}
module Data.ProtoLens.Compiler.Editions.Features
( featuresForEdition
, featuresForEditionFromDefaults
, mergedInto
) where

import Control.Applicative ((<|>))
import Data.ProtoLens (defMessage)
import Data.ProtoLens.Compiler.Editions.Defaults (nativeDefaults)
import Data.ProtoLens.Labels ()
import Lens.Family2 ((^.), (.~), (&))
import Proto.Google.Protobuf.Descriptor
( Edition
, FeatureSet
, FeatureSetDefaults)

{-|
Returns the native feature set defaults for the given edition.
Native features refer to the fields directly defined by 'FeatureSet'.
Features defined as extensions of 'FeatureSet' would be custom features.
-}
featuresForEdition :: Edition -> FeatureSet
featuresForEdition = featuresForEditionFromDefaults nativeDefaults

{-|
Given the feature set defaults for multiple editions,
return the feature set defaults for the given edition.
If extensions were supported, this could be used directly
to resolve custom features defined as extensions of 'FeatureSet'
for a particular edition.
-}
featuresForEditionFromDefaults :: FeatureSetDefaults -> Edition -> FeatureSet
featuresForEditionFromDefaults defaults edition
| (d : _) <- candidates = (d ^. #overridableFeatures) `mergedInto` (d ^. #fixedFeatures)
| otherwise = defMessage
where
candidates = dropWhile (\d -> d ^. #edition > edition) recentFirst

-- #defaults is supposed to be in ascending order of editions
recentFirst = reverse $ defaults ^. #defaults

{-|
Returns the result of merging a 'FeatureSet' message into another 'FeatureSet' message.
The semantics are the same as @MergeFrom@ in C++.
When merging a message A into a message B, then any field that has a value in A
will override the value of the same field in B,
otherwise the field in B is used as is.
Consider using this function as an infix operator.
For example,
>>> let c = a `mergedInto` b
could be read like "message C is the message A merged into message B".
If merging was generally supported for all messages,
we would use it directly instead of using this custom implementation for 'FeatureSet'.
This does not support merging extensions or unknown fields.
-}
mergedInto :: FeatureSet -- ^ Feature set to merge from.
-> FeatureSet -- ^ Feature set to merge into.
-> FeatureSet -- ^ The merged feature set.
mergedInto a b = defMessage
& #maybe'fieldPresence .~ (a ^. #maybe'fieldPresence <|> b ^. #maybe'fieldPresence)
& #maybe'enumType .~ (a ^. #maybe'enumType <|> b ^. #maybe'enumType)
& #maybe'repeatedFieldEncoding .~ (a ^. #maybe'repeatedFieldEncoding <|> b ^. #maybe'repeatedFieldEncoding)
& #maybe'utf8Validation .~ (a ^. #maybe'utf8Validation <|> b ^. #maybe'utf8Validation)
& #maybe'messageEncoding .~ (a ^. #maybe'messageEncoding <|> b ^. #maybe'messageEncoding)
& #maybe'jsonFormat .~ (a ^. #maybe'jsonFormat <|> b ^. #maybe'jsonFormat)
1 change: 1 addition & 0 deletions proto-lens-protoc/proto-lens-protoc.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ executable proto-lens-protoc
other-modules:
Data.ProtoLens.Compiler.Definitions
Data.ProtoLens.Compiler.Editions.Defaults
Data.ProtoLens.Compiler.Editions.Features
Data.ProtoLens.Compiler.Generate
Data.ProtoLens.Compiler.Generate.Commented
Data.ProtoLens.Compiler.Generate.Encoding
Expand Down

0 comments on commit 0298168

Please sign in to comment.