Download src/Assertica/Verify/Report.hs from Snapkitty/assertica: direct link, hf CLI and curl.
- Browser
- Download file 13.5 kB
-
https://huggingface.co/Snapkitty/assertica/resolve/main/src/Assertica/Verify/Report.hs
- Command line
-
hf download hf://Snapkitty/assertica/src/Assertica/Verify/Report.hs
-
curl -L -o Report.hs https://huggingface.co/Snapkitty/assertica/resolve/main/src/Assertica/Verify/Report.hs
13.5 kB
| {-| | |
| Module : Assertica.Verify.Report | |
| Description : Verification report structures and serialization | |
| Copyright : (c) 2026 Ahmad Ali Parr | |
| License : BSL-1.0 | |
| The VerificationReport captures the complete result of verifying a file or module, | |
| including the results at each pipeline stage, detailed errors, and the final verdict. | |
| DESIGN PRINCIPLES: | |
| 1. Comprehensive: Captures all verification state at each stage | |
| 2. Deterministic: Same input produces identical report | |
| 3. Machine-readable: JSON serialization for CI consumption | |
| 4. Human-readable: Clear text format with explanations | |
| 5. Actionable: Every error includes source location and context | |
| -} | |
| module Assertica.Verify.Report | |
| ( -- * Main report types | |
| VerificationReport (..) | |
| , StageResult (..) | |
| , Verdict (..) | |
| , SourceLocation (..) | |
| -- * Error types | |
| , ErrorDetail (..) | |
| , ErrorCategory (..) | |
| -- * Report builders and serialization | |
| , mkReport | |
| , mkStageResult | |
| , toJSON | |
| , toHumanReadable | |
| , toCSV | |
| -- * Utility functions | |
| , isSuccess | |
| , allStagesSuccessful | |
| , stagesToList | |
| ) where | |
| import Data.Text (Text) | |
| import qualified Data.Text as T | |
| import Data.List (intercalate) | |
| import Data.Time (UTCTime) | |
| import GHC.Generics (Generic) | |
| import Data.Ord (comparing) | |
| -- ============================================================================ | |
| -- SOURCE LOCATION | |
| -- ============================================================================ | |
| {-| Source location with file and line information. | |
| -} | |
| data SourceLocation = SourceLocation | |
| { slFile :: String | |
| , slLine :: Int | |
| , slColumn :: Int | |
| } | |
| deriving (Eq, Show, Generic) | |
| -- ============================================================================ | |
| -- ERROR TYPES | |
| -- ============================================================================ | |
| {-| Error category for classification. | |
| -} | |
| data ErrorCategory | |
| = SyntaxError -- ^ Parse failed (lexer/parser) | |
| | TypeError -- ^ Type checker rejected | |
| | ProofObligation -- ^ Proof missing or invalid | |
| | ModuleError -- ^ Circular dependency, missing import | |
| | TerminationError -- ^ Recursion not structurally decreasing | |
| | PositivityError -- ^ Inductive type not strictly positive | |
| | CodeGenError -- ^ Code generation failed | |
| | UnknownError -- ^ Unexpected error | |
| deriving (Eq, Show, Ord, Generic) | |
| {-| Detailed error information. | |
| -} | |
| data ErrorDetail = ErrorDetail | |
| { edCategory :: ErrorCategory | |
| , edMessage :: String | |
| , edLocation :: Maybe SourceLocation | |
| , edContext :: Maybe String -- ^ Source code context | |
| , edSuggestedFix :: Maybe String -- ^ Fix suggestion | |
| } | |
| deriving (Eq, Show, Generic) | |
| -- ============================================================================ | |
| -- STAGE RESULTS | |
| -- ============================================================================ | |
| {-| Result of a single verification stage. | |
| -} | |
| data StageResult | |
| = StageOK | |
| { srStageName :: String | |
| , srDuration :: Double -- ^ Milliseconds | |
| } | |
| | StageFailed | |
| { srStageName :: String | |
| , srDuration :: Double | |
| , srError :: ErrorDetail | |
| } | |
| deriving (Eq, Show, Generic) | |
| -- ============================================================================ | |
| -- FINAL VERDICT | |
| -- ============================================================================ | |
| {-| Final verification verdict. | |
| -} | |
| data Verdict | |
| = Accept -- ^ Module verified successfully | |
| | Reject -- ^ Module failed verification | |
| deriving (Eq, Show, Ord, Generic) | |
| -- ============================================================================ | |
| -- VERIFICATION REPORT | |
| -- ============================================================================ | |
| {-| Complete verification report for a file/module. | |
| -} | |
| data VerificationReport = VerificationReport | |
| { vrFile :: String -- ^ Input file path | |
| , vrModule :: Maybe String -- ^ Module name (if applicable) | |
| , vrRevision :: Maybe String -- ^ Git revision or hash | |
| , vrTimestamp :: String -- ^ ISO 8601 timestamp | |
| , vrParseResult :: StageResult -- ^ Lexer/Parser stage | |
| , vrElaborationResult :: StageResult -- ^ Elaboration stage | |
| , vrTypeCheckResult :: StageResult -- ^ Type checking stage | |
| , vrProofCheckResult :: StageResult -- ^ Proof verification stage | |
| , vrCodeGenResult :: StageResult -- ^ Code generation stage | |
| , vrVerdict :: Verdict -- ^ Final verdict | |
| , vrTotalDuration :: Double -- ^ Total time in milliseconds | |
| , vrAdditionalErrors :: [ErrorDetail] -- ^ Non-fatal errors or warnings | |
| } | |
| deriving (Eq, Show, Generic) | |
| -- ============================================================================ | |
| -- REPORT CONSTRUCTION | |
| -- ============================================================================ | |
| {-| Create a successful stage result. | |
| -} | |
| mkStageResult :: String -> Double -> StageResult | |
| mkStageResult name duration = StageOK name duration | |
| {-| Create a failed stage result. | |
| -} | |
| mkFailedStageResult :: String -> Double -> ErrorDetail -> StageResult | |
| mkFailedStageResult name duration err = StageFailed name duration err | |
| {-| Create a verification report. | |
| -} | |
| mkReport | |
| :: String -- ^ File path | |
| -> Maybe String -- ^ Module name | |
| -> Maybe String -- ^ Revision | |
| -> String -- ^ Timestamp | |
| -> StageResult -- ^ Parse result | |
| -> StageResult -- ^ Elaboration result | |
| -> StageResult -- ^ Type check result | |
| -> StageResult -- ^ Proof check result | |
| -> StageResult -- ^ Code gen result | |
| -> VerificationReport | |
| mkReport file modName rev ts pr er tr pcr cg = | |
| let stages = [pr, er, tr, pcr, cg] | |
| verdict = if all isSuccess stages then Accept else Reject | |
| totalDuration = sum [stageDuration s | s <- stages] | |
| in VerificationReport | |
| { vrFile = file | |
| , vrModule = modName | |
| , vrRevision = rev | |
| , vrTimestamp = ts | |
| , vrParseResult = pr | |
| , vrElaborationResult = er | |
| , vrTypeCheckResult = tr | |
| , vrProofCheckResult = pcr | |
| , vrCodeGenResult = cg | |
| , vrVerdict = verdict | |
| , vrTotalDuration = totalDuration | |
| , vrAdditionalErrors = [] | |
| } | |
| -- ============================================================================ | |
| -- QUERY FUNCTIONS | |
| -- ============================================================================ | |
| {-| Check if a stage result is successful. | |
| -} | |
| isSuccess :: StageResult -> Bool | |
| isSuccess StageOK{} = True | |
| isSuccess StageFailed{} = False | |
| {-| Check if all stages in a report were successful. | |
| -} | |
| allStagesSuccessful :: VerificationReport -> Bool | |
| allStagesSuccessful vr = | |
| all isSuccess | |
| [ vrParseResult vr | |
| , vrElaborationResult vr | |
| , vrTypeCheckResult vr | |
| , vrProofCheckResult vr | |
| , vrCodeGenResult vr | |
| ] | |
| {-| Get the duration of a stage result. | |
| -} | |
| stageDuration :: StageResult -> Double | |
| stageDuration StageOK{srDuration=d} = d | |
| stageDuration StageFailed{srDuration=d} = d | |
| {-| Get stage name. | |
| -} | |
| stageName :: StageResult -> String | |
| stageName StageOK{srStageName=n} = n | |
| stageName StageFailed{srStageName=n} = n | |
| {-| Convert report to list of stages. | |
| -} | |
| stagesToList :: VerificationReport -> [StageResult] | |
| stagesToList vr = | |
| [ vrParseResult vr | |
| , vrElaborationResult vr | |
| , vrTypeCheckResult vr | |
| , vrProofCheckResult vr | |
| , vrCodeGenResult vr | |
| ] | |
| -- ============================================================================ | |
| -- SERIALIZATION: JSON | |
| -- ============================================================================ | |
| {-| Serialize report to JSON format. | |
| Deterministic ordering ensures identical input produces identical JSON. | |
| -} | |
| toJSON :: VerificationReport -> String | |
| toJSON vr = | |
| "{\n" | |
| ++ " \"file\": \"" ++ escapeJSON (vrFile vr) ++ "\",\n" | |
| ++ " \"module\": " ++ maybe "null" (\m -> "\"" ++ escapeJSON m ++ "\"") (vrModule vr) ++ ",\n" | |
| ++ " \"revision\": " ++ maybe "null" (\r -> "\"" ++ escapeJSON r ++ "\"") (vrRevision vr) ++ ",\n" | |
| ++ " \"timestamp\": \"" ++ escapeJSON (vrTimestamp vr) ++ "\",\n" | |
| ++ " \"verdict\": \"" ++ show (vrVerdict vr) ++ "\",\n" | |
| ++ " \"totalDuration\": " ++ show (vrTotalDuration vr) ++ ",\n" | |
| ++ " \"stages\": [\n" | |
| ++ intercalate ",\n" (map stageToJSON (stagesToList vr)) | |
| ++ "\n ],\n" | |
| ++ " \"additionalErrors\": [\n" | |
| ++ intercalate ",\n" (map errorToJSON (vrAdditionalErrors vr)) | |
| ++ "\n ]\n" | |
| ++ "}" | |
| {-| Serialize a stage result to JSON. | |
| -} | |
| stageToJSON :: StageResult -> String | |
| stageToJSON (StageOK name duration) = | |
| " {\n" | |
| ++ " \"name\": \"" ++ escapeJSON name ++ "\",\n" | |
| ++ " \"status\": \"OK\",\n" | |
| ++ " \"duration\": " ++ show duration ++ "\n" | |
| ++ " }" | |
| stageToJSON (StageFailed name duration err) = | |
| " {\n" | |
| ++ " \"name\": \"" ++ escapeJSON name ++ "\",\n" | |
| ++ " \"status\": \"FAILED\",\n" | |
| ++ " \"duration\": " ++ show duration ++ ",\n" | |
| ++ " \"error\": " ++ errorToJSON err ++ "\n" | |
| ++ " }" | |
| {-| Serialize an error to JSON. | |
| -} | |
| errorToJSON :: ErrorDetail -> String | |
| errorToJSON err = | |
| "{\n" | |
| ++ " \"category\": \"" ++ show (edCategory err) ++ "\",\n" | |
| ++ " \"message\": \"" ++ escapeJSON (edMessage err) ++ "\",\n" | |
| ++ " \"location\": " ++ locationToJSON (edLocation err) ++ ",\n" | |
| ++ " \"context\": " ++ maybe "null" (\c -> "\"" ++ escapeJSON c ++ "\"") (edContext err) ++ ",\n" | |
| ++ " \"suggestedFix\": " ++ maybe "null" (\f -> "\"" ++ escapeJSON f ++ "\"") (edSuggestedFix err) ++ "\n" | |
| ++ " }" | |
| {-| Serialize source location to JSON. | |
| -} | |
| locationToJSON :: Maybe SourceLocation -> String | |
| locationToJSON Nothing = "null" | |
| locationToJSON (Just sl) = | |
| "{\n" | |
| ++ " \"file\": \"" ++ escapeJSON (slFile sl) ++ "\",\n" | |
| ++ " \"line\": " ++ show (slLine sl) ++ ",\n" | |
| ++ " \"column\": " ++ show (slColumn sl) ++ "\n" | |
| ++ " }" | |
| {-| Escape special JSON characters. | |
| -} | |
| escapeJSON :: String -> String | |
| escapeJSON = concatMap escapeChar | |
| where | |
| escapeChar '"' = "\\\"" | |
| escapeChar '\\' = "\\\\" | |
| escapeChar '\n' = "\\n" | |
| escapeChar '\r' = "\\r" | |
| escapeChar '\t' = "\\t" | |
| escapeChar c = [c] | |
| -- ============================================================================ | |
| -- SERIALIZATION: HUMAN-READABLE TEXT | |
| -- ============================================================================ | |
| {-| Serialize report to human-readable text format. | |
| -} | |
| toHumanReadable :: VerificationReport -> String | |
| toHumanReadable vr = | |
| unlines | |
| [ "==========================================" | |
| , "VERIFICATION REPORT" | |
| , "==========================================" | |
| , "" | |
| , "File: " ++ vrFile vr | |
| , "Module: " ++ maybe "(none)" id (vrModule vr) | |
| , "Revision: " ++ maybe "(unknown)" id (vrRevision vr) | |
| , "Timestamp: " ++ vrTimestamp vr | |
| , "" | |
| , "==========================================" | |
| , "VERIFICATION STAGES" | |
| , "==========================================" | |
| , "" | |
| , formatStages (stagesToList vr) | |
| , "" | |
| , "==========================================" | |
| , "FINAL VERDICT: " ++ show (vrVerdict vr) | |
| , "TOTAL TIME: " ++ show (vrTotalDuration vr) ++ " ms" | |
| , "==========================================" | |
| ] ++ (if null (vrAdditionalErrors vr) then "" else "\nADDITIONAL MESSAGES:\n" ++ formatErrors (vrAdditionalErrors vr)) | |
| {-| Format stages for human-readable output. | |
| -} | |
| formatStages :: [StageResult] -> String | |
| formatStages = unlines . zipWith formatStage [1..] | |
| where | |
| formatStage i (StageOK name duration) = | |
| " [" ++ show i ++ "] " ++ padTo 20 name ++ " : OK (" ++ show duration ++ " ms)" | |
| formatStage i (StageFailed name duration err) = | |
| " [" ++ show i ++ "] " ++ padTo 20 name ++ " : FAILED (" ++ show duration ++ " ms)\n" ++ | |
| " Error: " ++ edMessage err ++ "\n" ++ | |
| locString (edLocation err) | |
| {-| Format errors for human-readable output. | |
| -} | |
| formatErrors :: [ErrorDetail] -> String | |
| formatErrors = unlines . map formatError | |
| where | |
| formatError err = | |
| " - [" ++ show (edCategory err) ++ "] " ++ edMessage err | |
| ++ locString (edLocation err) | |
| ++ case edSuggestedFix err of | |
| Just fix -> "\n Suggestion: " ++ fix | |
| Nothing -> "" | |
| {-| Format location string. | |
| -} | |
| locString :: Maybe SourceLocation -> String | |
| locString Nothing = "" | |
| locString (Just sl) = | |
| " (at " ++ slFile sl ++ ":" ++ show (slLine sl) ++ ":" ++ show (slColumn sl) ++ ")" | |
| {-| Pad string to specified width. | |
| -} | |
| padTo :: Int -> String -> String | |
| padTo n s = s ++ replicate (max 0 (n - length s)) ' ' | |
| -- ============================================================================ | |
| -- SERIALIZATION: CSV (for batch reporting) | |
| -- ============================================================================ | |
| {-| Serialize report to CSV format (single row per file). | |
| -} | |
| toCSV :: VerificationReport -> String | |
| toCSV vr = | |
| escapeCSV (vrFile vr) ++ "," | |
| ++ escapeCSV (maybe "" id (vrModule vr)) ++ "," | |
| ++ escapeCSV (maybe "" id (vrRevision vr)) ++ "," | |
| ++ show (vrVerdict vr) ++ "," | |
| ++ show (vrTotalDuration vr) | |
| {-| CSV header for batch reports. | |
| -} | |
| csvHeader :: String | |
| csvHeader = "File,Module,Revision,Verdict,Duration" | |
| {-| Escape special CSV characters. | |
| -} | |
| escapeCSV :: String -> String | |
| escapeCSV s | |
| | any (`elem` s) ",\"\n" = "\"" ++ concatMap escapeChar s ++ "\"" | |
| | otherwise = s | |
| where | |
| escapeChar '"' = "\"\"" | |
| escapeChar c = [c] | |