model-0.5: src/Data/Model/Util.hs
{-# 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 ++ "."