{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE TypeSynonymInstances #-} module Data.Model.Util ( -- * Dependencies -- properMutualGroups -- , mutualGroups -- , transitiveClosure -- * Error utilities Errors , toErrors , noErrors , errsInContext , inContext , errorToConvertResult , errorsToConvertResult , convertResultToError , convertResultToErrors , convertOrError -- * Convertible re-exports , Convertible(..) , convert , ConvertResult , ConvertError(..) -- * Formatting utilities , dotted ) where import Data.Bifunctor import Data.Convertible import Data.Convertible.Tiny() import Data.Typeable import Data.List -- import Text.PrettyPrint.HughesPJClass (Pretty, prettyShow) -- |A list of error messages type Errors = [Error] type Error = String toErrors :: Bifunctor p => p a c -> p [a] c toErrors = first (: []) noErrors :: Errors -> Bool noErrors = null errorToConvertResult :: (Typeable b, Typeable a, Show a) => (a -> Either Error b) -> a -> ConvertResult b errorToConvertResult conv a = either (`convError` a) Right $ conv a {-| >>> errorsToConvertResult (const (Left ["Bad format","Invalid value"])) ".." :: ConvertResult Int Left (ConvertError {convSourceValue = "\"..\"", convSourceType = "[Char]", convDestType = "Int", convErrorMessage = "Bad format, Invalid value"}) -} errorsToConvertResult :: (Typeable b, Typeable t, Show t) => (t -> Either Errors b) -> t -> ConvertResult b errorsToConvertResult conv a = either (\errs -> convError (intercalate ", " errs) a) Right $ conv a {-| >>> import Data.Word >>> convertOrError 'a' :: Either Error Word Right 97 >>> convertOrError (1E50::Double) :: Either Error Word64 Left "Convertible: error converting source data 1.0e50 of type Double to type Word64: Input value outside of bounds: (0,18446744073709551615)" -} convertOrError :: Convertible a c => a -> Either String c convertOrError = convertResultToError . safeConvert convertResultToError :: Bifunctor p => p ConvertError c -> p String c convertResultToError = first prettyConvertError convertResultToErrors :: Bifunctor p => p ConvertError c -> p [String] c convertResultToErrors = toErrors . convertResultToError --instance Convertible String String where safeConvert = Right -- instance Convertible a a where safeConvert = Right -- |Prefix errors with a contextual note errsInContext :: (Convertible ctx String, Bifunctor p) => ctx -> p [String] c -> p [String] c errsInContext ctx = first (inContext ctx) {-|Prefix a list of strings with a contextual note >>> inContext "0/0" ["Zero denominator"] ["In 0/0: Zero denominator"] -} inContext :: Convertible ctx String => ctx -> [String] -> [String] inContext ctx = map (\msg -> unwords ["In", convert ctx ++ ":", msg]) {-| Intercalate a dot between the non empty elements of a list of strings. >>> dotted [] "" >>> dotted ["","bc","de"] "bc.de" >>> dotted ["bc","","de"] "bc.de" -} dotted :: [String] -> String -- dotted = intercalate "." . filter (not . null) dotted [] = "" dotted [s ] = s dotted (h:t) = post h ++ dotted t where post s | null s = "" | otherwise = s ++ "."