content: {-# language CPP #-}
  {-# language DataKinds #-}
  {-# language RankNTypes #-}
  {-# language ScopedTypeVariables #-}
  module Helpers where
  
  import Hedgehog
  
  import Control.Lens.Fold ((^?), folded)
  import Control.Monad (void)
  import Data.List.NonEmpty (NonEmpty(..))
  import Data.Semigroup (Semigroup)
  import Data.Text (Text)
  import Data.Validation (Validation(..), _Failure)
  import qualified Text.Megaparsec as Megaparsec
  import Text.Megaparsec.Pos (SourcePos(..), mkPos)
  
  import Language.Python.Internal.Lexer
    (SrcInfo, insertTabs, tokenize
    )
  import Language.Python.Internal.Token (PyToken)
  import Language.Python.Parse (Parser)
  import Language.Python.Parse.Error (ParseError, ErrorItem(..), _ParseError)
  import Language.Python.Internal.Parse (runParser)
  import Language.Python.Syntax.Expr (Expr)
  import Language.Python.Syntax.Module (Module)
  import Language.Python.Syntax.Statement (Statement)
  import Language.Python.Validate
  
  doTokenize :: Monad m => Text -> PropertyT m [PyToken SrcInfo]
  doTokenize input =
    case tokenize "test" input of
      Left err -> annotateShow (err :: ParseError SrcInfo) *> failure
      Right a -> pure a
  
  doTabs
    :: forall ann m
     . (Semigroup ann, Show ann, Monad m)
    => ann
    -> [PyToken ann]
    -> PropertyT m [PyToken ann]
  doTabs ann input =
    case insertTabs ann input of
      Left err -> annotateShow (err :: ParseError ann) *> failure
      Right a -> pure a
  
  doParse :: Monad m => Parser a -> [PyToken SrcInfo] -> PropertyT m a
  doParse pa input = do
    let res = runParser "test" pa input
    case res of
      Left err -> do
        annotateShow (err :: ParseError SrcInfo)
        failure
      Right a -> pure a
  
  syntaxValidateModule
    :: Module '[] ()
    -> PropertyT IO
         (Validation
            (NonEmpty (SyntaxError ()))
            (Module '[Syntax, Indentation] ()))
  syntaxValidateModule x =
    case runValidateIndentation $ validateModuleIndentation x of
      Failure errs -> do
        annotateShow (errs :: NonEmpty (IndentationError ()))
        failure
      Success a ->
        pure $ runValidateSyntax (validateModuleSyntax a)
  
  syntaxValidateStatement
    :: Statement '[] ()
    -> PropertyT IO
         (Validation
            (NonEmpty (SyntaxError ()))
            (Statement '[Syntax, Indentation] ()))
  syntaxValidateStatement x =
    case runValidateIndentation $ validateStatementIndentation x of
      Failure errs -> do
        annotateShow (errs :: NonEmpty (IndentationError ()))
        failure
      Success a ->
        pure $ runValidateSyntax (validateStatementSyntax a)
  
  syntaxValidateExpr
    :: Expr '[] ()
    -> PropertyT IO
         (Validation
            (NonEmpty (SyntaxError ()))
            (Expr '[Syntax, Indentation] ()))
  syntaxValidateExpr x =
    case runValidateIndentation $ validateExprIndentation x of
      Failure errs -> do
        annotateShow (errs :: NonEmpty (IndentationError ()))
        failure
      Success a ->
        pure $ runValidateSyntax (validateExprSyntax a)
  
  shouldBeFailure :: MonadTest m => Validation e a -> m ()
  shouldBeFailure res =
    case res of
      Success{} -> failure
      Failure{} -> success
  
  shouldBeSuccess :: (MonadTest m, Show e) => Validation e a -> m a
  shouldBeSuccess res =
    case res of
      Success a -> pure a
      Failure err -> do
        annotateShow err
        failure
  
  shouldBeParseSuccess
    :: MonadTest m
    => (FilePath -> Text -> Validation (NonEmpty (ParseError SrcInfo)) a)
    -> Text -> m a
  shouldBeParseSuccess p = shouldBeSuccess . p "test"
  
  shouldBeParseFailure
    :: MonadTest m
    => (FilePath -> Text -> Validation (NonEmpty (ParseError SrcInfo)) a)
    -> Text -> m ()
  shouldBeParseFailure p = shouldBeFailure . p "test"
  
  shouldBeParseError
    :: (MonadTest m, Show e, Show a)
    => Int
    -> Int
    -> PyToken ()
    -> Validation (NonEmpty (ParseError e)) a
    -> m ()
  shouldBeParseError line col tk res =
    case res ^? _Failure.folded._ParseError of
      Just
        (Megaparsec.ParseErrorBundle
          (  Megaparsec.TrivialError offset (Just (Tokens (errorItem :| []))) _
          :| _
          )
          posState
        ) -> do
        let
  #if MIN_VERSION_megaparsec(8,0,0)
          Megaparsec.PosState { Megaparsec.pstateSourcePos = srcPos } =
            Megaparsec.reachOffsetNoLine offset posState
  #else
          (srcPos, _) = Megaparsec.reachOffsetNoLine offset posState
  #endif
        sourceLine srcPos === mkPos line
        sourceColumn srcPos === mkPos col
  
        void errorItem === tk
      _ -> do
        annotateShow res
        failure
  
  shouldBeSyntaxError
    :: (MonadTest m, Show a)
    => SyntaxError ()
    -> Validation (NonEmpty (SyntaxError ())) a
    -> m ()
  shouldBeSyntaxError err res =
    case res ^? _Failure.folded of
      Just err' -> err === err'
      _ -> do
        annotateShow res
        failure
  