    {-# LANGUAGE CPP #-}
    {-
    Copyright (C) 2009-2011 John Goerzen <jgoerzen@complete.org>

    All rights reserved.

    For license and copyright information, see the file LICENSE

    -}

    {- |
       Module     : Data.Convertible.Base
       Copyright  : Copyright (C) 2009-2011 John Goerzen
       License    : BSD3

       Maintainer : John Goerzen <jgoerzen@complete.org>
       Stability  : provisional
       Portability: portable

    -}

    module Data.Convertible.Base( -- * The conversion process
                                  convert,
                                  Convertible(..),
                                  -- * Handling the results
                                  ConvertResult,
                                  ConvertError(..),
                                  convError,
                                  prettyConvertError
                                 )
    where
    #if !MIN_VERSION_mtl(2,3,0)
    import Control.Monad.Error
    #endif
    import Data.Typeable

    {- | The result of a safe conversion via 'safeConvert'. -}
    type ConvertResult a = Either ConvertError a

    ----------------------------------------------------------------------
    -- Conversions
    ----------------------------------------------------------------------

    {- | A typeclass that represents something that can be converted.
    A @Convertible a b@ instance represents an @a@ that can be converted to a @b@. -}
    class Convertible a b where
        {- | Convert @a@ to @b@, returning Right on success and Left on error.
           For a simpler interface, see 'convert'. -}
        safeConvert :: a -> ConvertResult b

    {-
    {- | Any type can be converted to itself. -}
    instance Convertible a a where
        safeConvert x = return x
    -}

    {-
    {- | Lists of any convertible type can be converted. -}
    instance Convertible a b => Convertible [a] [b] where
        safeConvert = mapM safeConvert
    -}

    {- | Convert from one type of data to another.  Raises an exception if there is
    an error with the conversion.  For a function that does not raise an exception
    in that case, see 'safeConvert'. -}
    convert :: Convertible a b => a -> b
    convert :: forall a b. Convertible a b => a -> b
    convert a
    x =
        case a -> ConvertResult b
    forall a b. Convertible a b => a -> ConvertResult b
    safeConvert a
    x of
          Left ConvertError
    e -> [Char] -> b
    forall a. HasCallStack => [Char] -> a
    error (ConvertError -> [Char]
    prettyConvertError ConvertError
    e)
          Right b
    r -> b
    r

    {-
    instance Convertible Int Double where
        safeConvert = return . fromIntegral
    instance Convertible Double Int where
        safeConvert = return . truncate         -- could do bounds checking here
    instance Convertible Integer Double where
        safeConvert = return . fromIntegral
    instance Convertible Double Integer where
        safeConvert = return . truncate
    -}

    ----------------------------------------------------------------------
    -- Error Handling
    ----------------------------------------------------------------------

    {- | How we indicate that there was an error. -}
    data ConvertError = ConvertError {
          ConvertError -> [Char]
    convSourceValue :: String,
          ConvertError -> [Char]
    convSourceType :: String,
          ConvertError -> [Char]
    convDestType :: String,
          ConvertError -> [Char]
    convErrorMessage :: String}
                        deriving (ConvertError -> ConvertError -> Bool
    (ConvertError -> ConvertError -> Bool)
    -> (ConvertError -> ConvertError -> Bool) -> Eq ConvertError
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: ConvertError -> ConvertError -> Bool
    == :: ConvertError -> ConvertError -> Bool
    $c/= :: ConvertError -> ConvertError -> Bool
    /= :: ConvertError -> ConvertError -> Bool
    Eq, ReadPrec [ConvertError]
    ReadPrec ConvertError
    Int -> ReadS ConvertError
    ReadS [ConvertError]
    (Int -> ReadS ConvertError)
    -> ReadS [ConvertError]
    -> ReadPrec ConvertError
    -> ReadPrec [ConvertError]
    -> Read ConvertError
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS ConvertError
    readsPrec :: Int -> ReadS ConvertError
    $creadList :: ReadS [ConvertError]
    readList :: ReadS [ConvertError]
    $creadPrec :: ReadPrec ConvertError
    readPrec :: ReadPrec ConvertError
    $creadListPrec :: ReadPrec [ConvertError]
    readListPrec :: ReadPrec [ConvertError]
    Read, Int -> ConvertError -> ShowS
    [ConvertError] -> ShowS
    ConvertError -> [Char]
    (Int -> ConvertError -> ShowS)
    -> (ConvertError -> [Char])
    -> ([ConvertError] -> ShowS)
    -> Show ConvertError
    forall a.
    (Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> ConvertError -> ShowS
    showsPrec :: Int -> ConvertError -> ShowS
    $cshow :: ConvertError -> [Char]
    show :: ConvertError -> [Char]
    $cshowList :: [ConvertError] -> ShowS
    showList :: [ConvertError] -> ShowS
    Show)

    #if !MIN_VERSION_mtl(2,3,0)
    instance Error ConvertError where
        strMsg :: [Char] -> ConvertError
    strMsg [Char]
    x = [Char] -> [Char] -> [Char] -> [Char] -> ConvertError
    ConvertError [Char]
    "(unknown)" [Char]
    "(unknown)" [Char]
    "(unknown)" [Char]
    x
    #endif

    convError' :: (Show a, Typeable a, Typeable b) =>
                   String -> a -> b -> ConvertResult b
    convError' :: forall a b.
    (Show a, Typeable a, Typeable b) =>
    [Char] -> a -> b -> ConvertResult b
    convError' [Char]
    msg a
    inpval b
    retval =
         ConvertError -> Either ConvertError b
    forall a b. a -> Either a b
    Left (ConvertError -> Either ConvertError b)
    -> ConvertError -> Either ConvertError b
    forall a b. (a -> b) -> a -> b
    $ ConvertError {
                 convSourceValue :: [Char]
    convSourceValue = a -> [Char]
    forall a. Show a => a -> [Char]
    show a
    inpval,
                 convSourceType :: [Char]
    convSourceType = TypeRep -> [Char]
    forall a. Show a => a -> [Char]
    show (TypeRep -> [Char]) -> (a -> TypeRep) -> a -> [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> TypeRep
    forall a. Typeable a => a -> TypeRep
    typeOf (a -> [Char]) -> a -> [Char]
    forall a b. (a -> b) -> a -> b
    $ a
    inpval,
                 convDestType :: [Char]
    convDestType = TypeRep -> [Char]
    forall a. Show a => a -> [Char]
    show (TypeRep -> [Char]) -> (b -> TypeRep) -> b -> [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . b -> TypeRep
    forall a. Typeable a => a -> TypeRep
    typeOf (b -> [Char]) -> b -> [Char]
    forall a b. (a -> b) -> a -> b
    $ b
    retval,
                 convErrorMessage :: [Char]
    convErrorMessage = [Char]
    msg}

    convError :: (Show a, Typeable a, Typeable b) =>
                 String -> a -> ConvertResult b
    convError :: forall a b.
    (Show a, Typeable a, Typeable b) =>
    [Char] -> a -> ConvertResult b
    convError [Char]
    msg a
    inpval =
        [Char] -> a -> b -> ConvertResult b
    forall a b.
    (Show a, Typeable a, Typeable b) =>
    [Char] -> a -> b -> ConvertResult b
    convError' [Char]
    msg a
    inpval b
    forall a. HasCallStack => a
    undefined

    prettyConvertError :: ConvertError -> String
    prettyConvertError :: ConvertError -> [Char]
    prettyConvertError (ConvertError [Char]
    sv [Char]
    st [Char]
    dt [Char]
    em) =
        [Char]
    "Convertible: error converting source data " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    sv [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " of type " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    st
        [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " to type " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    dt [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    ": " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    em