packages feed

tadka-2.0.0.0: test/props/Phase9.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Phase 9: the generics path derives only 'context', identically to a
-- hand-written 'buildContext' call. @genericContext :: e -> Context@ can, by its
-- type, touch nothing else — code/severity/help/url/message/diagnosticId stay at
-- their class defaults here, exactly as in the manual twin.
module Phase9 (group) where

import qualified Data.Aeson                 as A
import           Data.Text                  (Text)
import qualified Data.Text.Lazy             as TL
import qualified Data.Text.Lazy.Encoding    as TLE
import           GHC.Generics               (Generic)
import           Prettyprinter              (LayoutOptions (..), PageWidth (Unbounded),
                                             layoutPretty, pretty)
import           Prettyprinter.Render.Text  (renderStrict)

import           Hedgehog                   (Group (..), Property, property, withTests, (===))

import           Tadka
import           Tadka.Internal             (buildContext)

rightOrErr :: Show a => Either a b -> b
rightOrErr = either (error . show) id

-- Generics-wired: only message + context; context comes from genericContext,
-- which uses the Span field selector names ("gfrom", "gto") as label text.
data GErr = GErr { gsrc :: NamedSource, gfrom :: Span, gto :: Span }
  deriving (Generic)

instance Diagnostic GErr where
  message _ = "generic label wiring"
  context   = genericContext

-- Hand-written twin: same message, context via an explicit buildContext call
-- with the same label texts and order.
data MErr = MErr { msrc :: NamedSource, mfrom :: Span, mto :: Span }

instance Diagnostic MErr where
  message _ = "generic label wiring"
  context e = buildContext (msrc e)
    [ (mfrom e, Just (pretty ("gfrom" :: Text)))
    , (mto e,   Just (pretty ("gto"   :: Text)))
    ]

srcV :: NamedSource
srcV = rightOrErr (mkNamedSource "f.hs" "abcdefghij")

gVal :: GErr
gVal = GErr srcV (rightOrErr (mkSpan 0 3)) (rightOrErr (mkSpan 5 2))

mVal :: MErr
mVal = MErr srcV (rightOrErr (mkSpan 0 3)) (rightOrErr (mkSpan 5 2))

gfx :: Diagnostic e => e -> Text
gfx e = case selectRenderer (withColorMode ColorNever (withUnicodeMode UnicodeAlways (withTarget TGraphical defaultConfig))) of
  SomeRenderer r@(Graphical _) -> renderStrict (layoutPretty (LayoutOptions Unbounded) (render r e))
  _                            -> ""

nar :: Diagnostic e => e -> Text
nar e = case selectRenderer (withTarget TNarratable defaultConfig) of
  SomeRenderer r@(Narratable _) -> render r e
  _                             -> ""

jsn :: Diagnostic e => e -> Text
jsn e = case selectRenderer (withTarget TJson defaultConfig) of
  SomeRenderer r@(Json _) -> TL.toStrict (TLE.decodeUtf8 (A.encode (render r e)))
  _                       -> ""

once :: (Text, Text) -> Property
once (a, b) = withTests 1 (property (a === b))

group :: Group
group = Group "Phase 9 - generics context-wiring (derived == manual)"
  [ ("graphical renders identically",  once (gfx gVal, gfx mVal))
  , ("narratable renders identically", once (nar gVal, nar mVal))
  , ("json renders identically",       once (jsn gVal, jsn mVal))
  ]