packages feed

postgresql-syntax-0.5.0.0: library-internal/PostgresqlSyntax/Ast/TargetEl.hs

module PostgresqlSyntax.Ast.TargetEl where

import qualified HeadedMegaparsec as Parser
import {-# SOURCE #-} PostgresqlSyntax.Ast.AExpr (AExpr)
import {-# SOURCE #-} qualified PostgresqlSyntax.Ast.AExpr as AExpr
import PostgresqlSyntax.Ast.Ident
import qualified PostgresqlSyntax.Helpers.Gens as Gens
import qualified PostgresqlSyntax.Helpers.Parsers as Parsers
import PostgresqlSyntax.IsAst
import qualified PostgresqlSyntax.KeywordSet as KeywordSet
import PostgresqlSyntax.Prelude
import qualified Test.QuickCheck as Qc

-- |
-- ==== References
-- @
-- target_el:
--   |  a_expr AS ColLabel
--   |  a_expr IDENT
--   |  a_expr
--   |  '*'
-- @
data TargetEl
  = AliasedExprTargetEl AExpr Ident
  | ImplicitlyAliasedExprTargetEl AExpr Ident
  | ExprTargetEl AExpr
  | AsteriskTargetEl
  deriving (Show, Generic, Eq, Ord, Data)

instance IsAst TargetEl where
  toTextBuilder settings = \case
    AliasedExprTargetEl a b -> toTextBuilder settings a <> " AS " <> toTextBuilder settings b
    ImplicitlyAliasedExprTargetEl a b -> toTextBuilder settings a <> " " <> toTextBuilder settings b
    ExprTargetEl a -> toTextBuilder settings a
    AsteriskTargetEl -> "*"

  parser settings =
    Parser.label "target" $
      asum
        [ do
            expr <- parser settings
            asum
              [ do
                  Parsers.space1
                  asum
                    [ AliasedExprTargetEl expr <$> (Parsers.keyword "as" *> Parsers.space1 *> Parser.endHead *> colLabel),
                      ImplicitlyAliasedExprTargetEl expr <$> parser settings
                    ],
                pure (ExprTargetEl expr)
              ],
          AsteriskTargetEl <$ Parsers.char '*'
        ]
    where
      -- Duplicated from "PostgresqlSyntax.Parsing"'s @colLabel@ (a
      -- bare-aliased 'PostgresqlSyntax.Ast.Ident' whose own, more
      -- permissive parser lives above this module in the dependency
      -- order), mirroring the 'PostgresqlSyntax.Ast.AnyName' precedent.
      colLabel =
        Parser.label "column label" $
          Parsers.keywordNameFromSet UnquotedIdent KeywordSet.keyword
            <|> parser settings

instance Qc.Arbitrary TargetEl where
  shrink = Qc.genericShrink
  arbitrary =
    Qc.oneof
      [ pure AsteriskTargetEl,
        AliasedExprTargetEl <$> Gens.downscale Qc.arbitrary <*> Qc.arbitrary,
        -- Unlike 'AliasedExprTargetEl' (separated from its alias by the
        -- reserved @AS@ Parsers.keyword) or 'ExprTargetEl' (followed only by a
        -- comma\/end of list, neither valid @a_expr@ continuations), the
        -- expr here is followed directly by a bare alias identifier with
        -- nothing but a space — exactly the hazard
        -- 'PostgresqlSyntax.Ast.AExpr.isBoundedAExprOperand' guards
        -- against (e.g. rendering an 'PostgresqlSyntax.Ast.AExpr.OrAExpr'
        -- bare here would let its right operand absorb the alias).
        ImplicitlyAliasedExprTargetEl <$> AExpr.safeAExprOperand (Gens.downscale Qc.arbitrary) <*> Qc.arbitrary,
        ExprTargetEl <$> Gens.downscale Qc.arbitrary
      ]