module Dino.Verification where

import Prelude

import Data.Text (Text)
import qualified Data.Text as Text
import Text.PrettyPrint.ANSI.Leijen (Doc)
import qualified Text.PrettyPrint.ANSI.Leijen as PP

import Dino.AST.Diff
import Dino.Interpretation

-- | Check each 'assertEq' assertion by structurally comparing the ASTs of the
-- two expressions
--
-- This limited form of formal verification is useful two ways:
--
-- * It can be used to verify refactorings that don't affect the AST; for
--   example, introducing a meta-level helper function (i.e. a normal Haskell
--   function).
--
-- * When the ASTs do differ, the resulting 'Edit' will show only the parts of
--   the expressions where the difference occurs, which can make it easier to
--   manually verify equivalence.
verifyAssertEqStructurally ::
     CollectAssertions Reified a -> [(Text, Maybe (Edit NumRep))]
verifyAssertEqStructurally e =
  [ (lab, diff (unReified ref) (unReified act))
  | (lab, AssertEq ref act) <- as
  ]
  where
    as = fold $ prodSnd $ prodSnd $ unIntensional $ unCollectAssertions e

-- | Present the output of 'verifyAssertEqStructurally' as a list of test cases
-- that either succeed or fail with a diff
presentStructuralVerificationAsDocs :: [(Text, Maybe (Edit NumRep))] -> [Doc]
presentStructuralVerificationAsDocs as = map mkCase as
  where
    l  = maximum (0 : map (Text.length . fst) as)
    l' = min l 20

    showLabel lab =
      Text.unpack lab ++ ":" ++ replicate (l' - Text.length lab) ' '

    mkCase (lab, d) =
      PP.string (showLabel lab) <> PP.space <> diffAsTestResult d <> end
      where
        end = maybe mempty (const $ mempty PP.<$> mempty) d

presentStructuralVerification :: [(Text, Maybe (Edit NumRep))] -> IO ()
presentStructuralVerification =
  PP.putDoc . PP.vsep . presentStructuralVerificationAsDocs