{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Control.Teardown.Internal.Types where import RIO import RIO.Time (NominalDiffTime) #if MIN_VERSION_base(4,9,0) import GHC.Generics (Generic) #endif import Data.Text.Prettyprint.Doc (Pretty, pretty, (<+>)) import qualified Data.Text.Prettyprint.Doc as Pretty -------------------------------------------------------------------------------- type Description = Text -- | Result from a 'Teardown' sub-routine data TeardownResult -- | Result is composed by multiple teardown sub-routines = BranchResult { -- | Text description of parent teardown spec resultDescription :: !Description -- | Sum of elapsed time on sub-routines execution , resultElapsedTime :: !NominalDiffTime -- | Tells if any sub-routines failed , resultDidFail :: !Bool -- | Results of inner sub-routines , resultListing :: ![TeardownResult] } -- | Result represents a single teardown sub-routine | LeafResult { -- | Text description of sub-routine resultDescription :: !Description -- | Elapsed time on sub-routine execution , resultElapsedTime :: !NominalDiffTime -- | Exception from sub-routine , resultError :: !(Maybe SomeException) } -- | Represents a stub cleanup operation (for lifting pure values) | EmptyResult { -- | Text description of faked sub-routine resultDescription :: !Description } deriving (Generic, Show) instance Pretty TeardownResult where pretty result = case result of EmptyResult {resultDescription} -> "✓" <+> pretty resultDescription <+> Pretty.parens "empty" LeafResult {resultDescription, resultElapsedTime, resultError} -> case resultError of Nothing -> "✓" <+> pretty resultDescription <+> Pretty.parens (pretty $ show resultElapsedTime) Just err -> Pretty.hang 2 (Pretty.vsep ["✘" <+> pretty resultDescription <+> Pretty.parens (pretty $ show resultElapsedTime) , "|" <+> pretty (show err) ]) BranchResult {resultDidFail, resultDescription, resultElapsedTime, resultListing} -> let symbolDoc = if resultDidFail then "✘" else "✓" in symbolDoc <+> pretty resultDescription <+> Pretty.parens (pretty $ show resultElapsedTime) <> Pretty.hardline <> Pretty.indent 2 (Pretty.align (Pretty.vsep (map pretty resultListing))) instance Display TeardownResult where display = displayShow . pretty getElapsedTime :: TeardownResult -> NominalDiffTime getElapsedTime result = case result of BranchResult {resultElapsedTime} -> resultElapsedTime LeafResult {resultElapsedTime} -> resultElapsedTime EmptyResult {} -> 0 instance NFData TeardownResult where rnf result = case result of EmptyResult !_desc -> () LeafResult !_desc !_time !_err -> () BranchResult !_desc !_time !_didFail listing -> rnf listing -- | Sub-routine that performs a resource cleanup operation newtype Teardown = Teardown (IO TeardownResult) deriving (Generic) instance NFData Teardown where rnf !_ = () -- | A record that __is__ or __contains__ a 'Teardown' sub-routine should -- instantiate this typeclass class HasTeardown teardown where -- | Executes teardown sub-routine returning a "TeardownResult" getTeardown :: teardown -> Teardown -- | A resource or sub-routine that can be transformed into a 'Teardown' -- operation class IResource resource where newTeardown :: Text -> resource -> IO Teardown