diff --git a/proto-lens-protoc/app/Data/ProtoLens/Compiler/Editions/Defaults.hs b/proto-lens-protoc/app/Data/ProtoLens/Compiler/Editions/Defaults.hs new file mode 100644 index 00000000..d464f81b --- /dev/null +++ b/proto-lens-protoc/app/Data/ProtoLens/Compiler/Editions/Defaults.hs @@ -0,0 +1,45 @@ +-- 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.Defaults +Description: Exports defaults for native features in Protobuf Editions. +Copyright: Copyright (c) 2024 Google LLC +License: BSD3 +-} +module Data.ProtoLens.Compiler.Editions.Defaults (nativeDefaults) where + +import Data.ByteString (ByteString) +import Data.ProtoLens (decodeMessage) +import Proto.Google.Protobuf.Descriptor (FeatureSetDefaults) + +{-| 'FeatureSetDefaults' message containing feature defaults. + +This contains the defaults from editions @LEGACY@ to @EDITION_2023@ +for the native features defined by @google.protobuf.FeatureSet@. +-} +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. + +This contains the defaults from editions @LEGACY@ to @EDITION_2023@ +for the native features defined by @google.protobuf.FeatureSet@. +The message was generated with @protoc@ and translated into a Haskell string: + +> $ protoc --edition_defaults_out=defaults.binpb --edition_defaults_minimum=LEGACY --edition_defaults_maximum=2023 google/protobuf/descriptor.proto +> $ ghci +> ghci> import Data.ByteString as B +> ghci> B.readFile "defaults.binpb" >>= print . show + +-} +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\"" diff --git a/proto-lens-protoc/app/Data/ProtoLens/Compiler/Editions/Features.hs b/proto-lens-protoc/app/Data/ProtoLens/Compiler/Editions/Features.hs new file mode 100644 index 00000000..9f523a88 --- /dev/null +++ b/proto-lens-protoc/app/Data/ProtoLens/Compiler/Editions/Features.hs @@ -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) diff --git a/proto-lens-protoc/proto-lens-protoc.cabal b/proto-lens-protoc/proto-lens-protoc.cabal index 0d1ac61c..e30c5f63 100644 --- a/proto-lens-protoc/proto-lens-protoc.cabal +++ b/proto-lens-protoc/proto-lens-protoc.cabal @@ -1,6 +1,6 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.36.0. +-- This file has been generated from package.yaml by hpack version 0.37.0. -- -- see: https://github.com/sol/hpack @@ -42,6 +42,8 @@ executable proto-lens-protoc main-is: protoc-gen-haskell.hs 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