module Abstract.Valid
( Valid(..)
, ValidationResult
, ensure
, withContext
, errorMessages
, validateNamed
, ensureValid
) where
import qualified Data.List as List
import System.Exit
class Valid a where
isValid :: a -> Bool
isValid x =
case validate x of
IsValid -> True
IsInvalid _ -> False
validate :: a -> ValidationResult
validate x =
if isValid x then
IsValid
else
IsInvalid ["<invalid value>"]
{-# MINIMAL isValid | validate #-}
data ValidationResult
= IsValid
| IsInvalid [String]
instance Monoid ValidationResult where
mempty = IsValid
mappend IsValid IsValid = IsValid
mappend IsValid (IsInvalid msgs) = IsInvalid msgs
mappend (IsInvalid msgs) IsValid = IsInvalid msgs
mappend (IsInvalid msgs1) (IsInvalid msgs2) = IsInvalid (msgs1 ++ msgs2)
ensure :: Bool -> String -> ValidationResult
ensure True _ = IsValid
ensure False message = IsInvalid [message]
withContext :: String -> ValidationResult -> ValidationResult
withContext _ IsValid =
IsValid
withContext context (IsInvalid messages) =
IsInvalid (map prependContext messages)
where
prependContext msg = context ++ ": " ++ msg
errorMessages :: ValidationResult -> Maybe String
errorMessages IsValid = Nothing
errorMessages (IsInvalid msgs) = Just (List.intercalate "\n" msgs)
validateNamed :: Valid a => (name -> String) -> [(name, a)] -> ValidationResult
validateNamed nameToContext items =
mconcat (map validateItem items)
where
validateItem (name, item) =
withContext (nameToContext name) (validate item)
ensureValid :: ValidationResult -> IO ()
ensureValid result =
case errorMessages result of
Nothing -> return ()
Just messages -> putStrLn messages >> exitFailure