mmzk-typeid-0.7.0.0: src/Data/KindID/Error.hs
{-# LANGUAGE UndecidableInstances #-}
-- | An internal module to provide better compilation errors for invalid
-- 'Data.KindID.V7.KindID' prefixes.
--
module Data.KindID.Error where
import Data.Kind
import Data.TypeID.Error
import Data.Type.Bool
import Data.Type.Equality
import Data.Type.Ord
import GHC.TypeLits
-- | A class that covnerts a poly-kind into an "ErrorMessage".
--
-- Used for converting type-level "TypeIDError"s into custom compile-time error
-- messages.
class ToErrorMessageC (e :: k) where
type ToErrorMessage e :: ErrorMessage
instance ToErrorMessageC (TypeIDErrorPrefixTooLong t) where
type ToErrorMessage (TypeIDErrorPrefixTooLong t) =
Text "The prefix "
:<>: ShowType (Str2Sym t)
:<>: Text " with "
:<>: ShowType (LengthSymbol (Str2Sym t))
:<>: Text " characters is too long!"
instance ToErrorMessageC TypeIDExtraSeparator where
type ToErrorMessage TypeIDExtraSeparator = Text
"The underscore separator should not be present if the prefix is empty!"
instance ToErrorMessageC (TypeIDStartWithUnderscore t) where
type ToErrorMessage (TypeIDStartWithUnderscore t) =
Text "The prefix "
:<>: ShowType (Str2Sym t)
:<>: Text " should not start with an underscore!"
instance ToErrorMessageC (TypeIDEndWithUnderscore t) where
type ToErrorMessage (TypeIDEndWithUnderscore t) =
Text "The prefix "
:<>: ShowType (Str2Sym t)
:<>: Text " should not end with an underscore!"
instance ToErrorMessageC (TypeIDErrorPrefixInvalidChar t c) where
type ToErrorMessage (TypeIDErrorPrefixInvalidChar t c) =
Text "The prefix "
:<>: ShowType (Str2Sym t)
:<>: Text " contains invalid character "
:<>: ShowType c
:<>: Text "!"
type family Str2Sym (str :: String) :: Symbol where
Str2Sym '[] = ""
Str2Sym (c ': cs) = ConsSymbol c (Str2Sym cs)
type family Sym2Str (str :: Symbol) :: String where
Sym2Str s = SSUH (UnconsSymbol s)
type family SSUH (uncons :: Maybe (Char, Symbol)) :: String where
SSUH Nothing = '[]
SSUH (Just '( c, s )) = c ': Sym2Str s
-- | The length of a 'Symbol' as a 'Nat'.
type family LengthSymbol (prefix :: Symbol) :: Nat where
LengthSymbol prefix = LSUH (UnconsSymbol prefix)
-- | LengthSymbol Uncons Helper.
type family LSUH (uncons :: Maybe (Char, Symbol)) :: Nat where
LSUH 'Nothing = 0
LSUH ('Just '( c, s )) = 1 + LengthSymbol s
-- | Is a type-level 'Char' lowercase?
type family IsLowerChar (ch :: Char) :: Bool where
IsLowerChar ch = Compare '`' ch == 'LT && Compare ch '{' == 'LT
-- | Is a type-level 'Char' an underscore?
type family IsUnderscore (ch :: Char) :: Bool where
IsUnderscore ch = Compare '_' ch == 'EQ
-- | Is a 'Symbol' lowercase + underscore and not start or end with underscores?
--
-- ''Nothing' if no error; otherwise ''Just' a 'TypeIDError'.
type family IsLUSymbol (prefix :: Symbol) :: Maybe TypeIDError where
IsLUSymbol "_" = 'Just 'TypeIDExtraSeparator
IsLUSymbol prefix = ILUSUH1 (UnconsSymbol prefix) prefix
-- | First IsLUSymbol Uncons Helper.
type family ILUSUH1 (uncons :: Maybe (Char, Symbol)) (prefix :: Symbol)
:: Maybe TypeIDError where
ILUSUH1 'Nothing s = 'Nothing
ILUSUH1 ('Just '( '_', _ )) s = 'Just
(TypeIDStartWithUnderscore (Sym2Str s))
ILUSUH1 ('Just '( c, r )) s =
WrapMaybe (IsLowerChar c || IsUnderscore c)
(TypeIDErrorPrefixInvalidChar (Sym2Str s) c)
<|> ILUSUH2 (UnconsSymbol r) s
-- | Second IsLUSymbol Uncons Helper.
type family ILUSUH2 (uncons :: Maybe (Char, Symbol)) (prefix :: Symbol)
:: Maybe TypeIDError where
ILUSUH2 'Nothing s = 'Nothing
ILUSUH2 ('Just '( '_', "" )) s = 'Just (TypeIDEndWithUnderscore (Sym2Str s))
ILUSUH2 ('Just '( c, r )) s =
WrapMaybe (IsLowerChar c || IsUnderscore c)
(TypeIDErrorPrefixInvalidChar (Sym2Str s) c)
<|> ILUSUH2 (UnconsSymbol r) s
-- | @wrapMaybe True _ = Nothing; wrapMaybe False x = Just x@.
type family WrapMaybe p a where
WrapMaybe True a = 'Nothing
WrapMaybe False a = 'Just a
-- | Type-level '(<|>)' for 'Maybe'.
type family (<|>) a b where
'Just a <|> b = 'Just a
Nothing <|> b = b
-- | Turn a type-level 'Maybe' 'TypeIDError' into a 'TypeError'.
type family BuildTypeIDErrorConstraint (a :: Maybe TypeIDError)
:: Constraint where
BuildTypeIDErrorConstraint 'Nothing = ()
BuildTypeIDErrorConstraint ('Just e) = TypeError (ToErrorMessage e)