packages feed

wireform-proto-0.3.0.0: src/Proto/Internal/FieldPresence.hs

{- | Resolve Editions field presence before either generator chooses field
carriers. The public parser retains the original source-level labels.
-}
module Proto.Internal.FieldPresence (resolveFieldPresence) where

import Data.List (find)
import Data.Text (Text)
import Proto.IDL.AST
import Proto.IDL.Annotations (lookupSimpleOption)


resolveFieldPresence :: ProtoFile -> Either Text ProtoFile
resolveFieldPresence pf = case protoSyntax pf of
  Editions edition -> do
    filePresence <- presenceOption (featureFieldPresence (featuresForEdition edition)) (protoOptions pf)
    topLevels <- traverse (topLevel filePresence) (protoTopLevels pf)
    pure pf {protoTopLevels = topLevels}
  _ -> Right pf
  where
    topLevel presence = \case
      TLMessage msg -> TLMessage <$> message presence msg
      TLExtend name fields -> TLExtend name <$> traverse (field presence) fields
      other -> Right other
    message presence msg = do
      elements <- traverse (element presence) (msgElements msg)
      pure msg {msgElements = elements}
    element presence = \case
      MEField fd -> MEField <$> field presence fd
      MEMessage msg -> MEMessage <$> message presence msg
      MEExtend name fields -> MEExtend name <$> traverse (field presence) fields
      other -> Right other
    field presence fd = case fieldLabel fd of
      Just Repeated -> Right fd
      _ -> do
        resolved <- presenceOption presence (fieldOptions fd)
        pure
          fd
            { fieldLabel = case resolved of
                ExplicitPresence -> Just Optional
                ImplicitPresence -> Nothing
                LegacyRequired -> Just Required
            }


-- Field presence is scoped to files and fields, not enclosing messages.
-- Accept both dotted and aggregate feature-option spellings.
presenceOption :: FieldPresenceFeature -> [OptionDef] -> Either Text FieldPresenceFeature
presenceOption fallback opts = case setting of
  Nothing -> Right fallback
  Just (CIdent "EXPLICIT") -> Right ExplicitPresence
  Just (CIdent "IMPLICIT") -> Right ImplicitPresence
  Just (CIdent "LEGACY_REQUIRED") ->
    Left "features.field_presence = LEGACY_REQUIRED is not supported by the code generators"
  Just _ -> Left "features.field_presence must be EXPLICIT or IMPLICIT"
  where
    setting = case find isPresence opts of
      Just option -> Just (optValue option)
      Nothing -> case lookupSimpleOption "features" opts of
        Just (CAggregate values) -> lookup "field_presence" values
        _ -> Nothing

    isPresence option = case optNameParts (optName option) of
      [SimpleOption "features", SimpleOption "field_presence"] -> True
      [SimpleOption "features.field_presence"] -> True
      _ -> False