packages feed

canontra-0.1.0.0: src/Canontra/Comparison/Compare.hs

{- |
Module      : Canontra.Comparison.Compare
Description : Comparison of fingerprints across files and bundles.

Comparison computes the delta across all 8 fingerprint tiers.
It reveals exactly where two programs diverge: whether in raw text,
computational structure, declaration contracts, dependency usage,
call graphs, control-flow (CFG), or data-flow (DFG).
-}
module Canontra.Comparison.Compare
  ( compareBundles
  , compareFingerprints
  , compareBundlesWithSeverity
  , compareFiles
  , formatComparisonResult
  ) where

import qualified Data.ByteString as BS
import qualified Data.Text as T
import qualified Data.Text.IO as TIO

import Canontra.Analysis.Impact (ChangeSeverity, classifySeverity)
import Canontra.Fingerprint.Bundle (computeBundle)
import Canontra.Types

-- | Alias for 'compareBundles' aligning with 9-tier comparison API.
compareFingerprints :: FingerprintBundle -> FingerprintBundle -> ComparisonResult
compareFingerprints = compareBundles

compareBundles :: FingerprintBundle -> FingerprintBundle -> ComparisonResult
compareBundles b1 b2 = ComparisonResult
  { crSource       = if f0Source b1 == f0Source b2 then Identical else Different
  , crStructural   = if f1Structural b1 == f1Structural b2 then Identical else Different
  , crDeclaration  = if f2Declaration b1 == f2Declaration b2 then Identical else Different
  , crDependency   = if f3Dependency b1 == f3Dependency b2 then Identical else Different
  , crCallGraph    = if fCGCallGraph b1 == fCGCallGraph b2 then Identical else Different
  , crControlFlow  = if fCFControlFlow b1 == fCFControlFlow b2 then Identical else Different
  , crDataFlow     = if fDFDataFlow b1 == fDFDataFlow b2 then Identical else Different
  , crTypeContract = if fTTypeContract b1 == fTTypeContract b2 then Identical else Different
  , crComposite    = if f4Composite b1 == f4Composite b2 then Identical else Different
  }

-- | Compare two bundles and classify the semantic severity of their delta.
compareBundlesWithSeverity :: FingerprintBundle -> FingerprintBundle -> (ComparisonResult, ChangeSeverity)
compareBundlesWithSeverity b1 b2 =
  (compareBundles b1 b2, classifySeverity b1 b2)

compareFiles :: FilePath -> FilePath -> IO (Either ParseError ComparisonResult)
compareFiles path1 path2 = do
  bytes1 <- BS.readFile path1
  bytes2 <- BS.readFile path2
  text1 <- TIO.readFile path1
  text2 <- TIO.readFile path2
  case (computeBundle path1 bytes1 text1, computeBundle path2 bytes2 text2) of
    (Left err, _) -> pure (Left err)
    (_, Left err) -> pure (Left err)
    (Right b1, Right b2) -> pure (Right (compareBundles b1 b2))

formatComparisonResult :: ComparisonResult -> T.Text
formatComparisonResult cr =
  T.unlines
    [ "================================================================================"
    , "  CANONTRA MULTI-TIER SEMANTIC INVARIANT COMPARISON"
    , "================================================================================"
    , "  Tier                               Status         Diagnostic Assessment"
    , "--------------------------------------------------------------------------------"
    , "  F0  (Source Code):                 " <> padStatus (crSource cr) <> diagF0 (crSource cr)
    , "  F1  (Normalized AST):              " <> padStatus (crStructural cr) <> diagF1 (crStructural cr)
    , "  F2  (Declaration Hierarchy):       " <> padStatus (crDeclaration cr) <> diagF2 (crDeclaration cr)
    , "  F3  (Dependency Graph):            " <> padStatus (crDependency cr) <> diagF3 (crDependency cr)
    , "  FCG (Intra-Module Call Graph):     " <> padStatus (crCallGraph cr) <> diagFCG (crCallGraph cr)
    , "  FCF (Control-Flow Graph):          " <> padStatus (crControlFlow cr) <> diagFCF (crControlFlow cr)
    , "  FDF (Data-Flow SSA Graph):         " <> padStatus (crDataFlow cr) <> diagFDF (crDataFlow cr)
    , "  FT  (Type Contract):               " <> padStatus (crTypeContract cr) <> diagFT (crTypeContract cr)
    , "--------------------------------------------------------------------------------"
    , "  F4  (Composite Invariant Hash):    " <> padStatus (crComposite cr) <> diagF4 (crComposite cr)
    , "================================================================================"
    ]
  where
    padStatus Identical = "IDENTICAL      "
    padStatus Different = "DIFFERENT      "

    diagF0 Identical = "Bit-identical source text"
    diagF0 Different = "Formatting, comments, or trivia altered"

    diagF1 Identical = "Normalized AST structure invariant"
    diagF1 Different = "Syntactic or semantic structure altered"

    diagF2 Identical = "Public declaration signatures invariant"
    diagF2 Different = "Public signatures, types, or members altered"

    diagF3 Identical = "Module dependencies invariant"
    diagF3 Different = "Imported symbols or dependency usage altered"

    diagFCG Identical = "Internal call topology invariant"
    diagFCG Different = "Caller/callee edge relationships altered"

    diagFCF Identical = "Control-flow branching invariant"
    diagFCF Different = "Basic block branching or decision logic altered"

    diagFDF Identical = "Data-flow Def-Use chains invariant"
    diagFDF Different = "Variable definition or taint paths altered"

    diagFT Identical = "Structural type contracts invariant"
    diagFT Different = "Interface shapes, methods, or type contracts altered"

    diagF4 Identical = "SEMANTICALLY EQUIVALENT (Safe to reuse build/cache)"
    diagF4 Different = "SEMANTIC DIVERGENCE (Rebuild required)"