{-# LANGUAGE FlexibleInstances #-} -- | -- Module : Data.PHPSession.Conv -- Copyright: (c) 2014 Edward Blake -- License: BSD-style -- Maintainer: Edward L. Blake -- Stability: experimental -- Portability: portable -- -- Non-coerced translation between 'PHPSessionValue' and various Haskell types. -- 'convTo' provide convenient translation from native types to 'PHPSessionValue', -- while translation from 'PHPSessionValue' to native types is provided through -- 'convFrom' and 'convFromSafe'. -- module Data.PHPSession.Conv ( -- * Convert to 'PHPSessionValue' convTo, -- * Convert from 'PHPSessionValue' convFrom, convFromSafe, -- * Type classes ConversionToPHPValue(..), ConversionFromPHPValueOrMismatch(..) ) where import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Data.ByteString.Char8 as BS import Data.PHPSession.Types import Data.Int (Int32, Int64) import Data.List as L (foldl') -- | 'convTo' is a convenience function that converts natively typed -- values to 'PHPSessionValue', with the resulting PHP type determined -- by the type cast or inferred. -- -- > arrayOfPHPStrings :: PHPSessionValue -- > arrayOfPHPStrings = -- > let str1 = "Hello" :: BS.ByteString -- > str2 = "World" -- > in convTo [(0 :: Int, str1), (1, str2)] -- -- In the above example code, the @OverloadedStrings@ language extension is assumed. -- convTo :: ConversionToPHPValue a => a -> PHPSessionValue convTo val = convTo' val class ConversionToPHPValue a where convTo' :: a -> PHPSessionValue class ConversionFromPHPValueOrMismatch b where convFromOM :: PHPSessionValue -> Either String b instance ConversionToPHPValue PHPSessionValue where convTo' var = var instance ConversionToPHPValue [(PHPSessionValue,PHPSessionValue)] where convTo' var = PHPSessionValueArray var instance (ConversionToPHPValue a, ConversionToPHPValue b) => ConversionToPHPValue [(a,b)] where convTo' var = PHPSessionValueArray $ map (\(al,ar) -> (convTo' al, convTo' ar)) var instance ConversionToPHPValue Bool where convTo' var = PHPSessionValueBool var instance ConversionToPHPValue Double where convTo' var = PHPSessionValueFloat (Right var) instance ConversionToPHPValue Int where convTo' var = PHPSessionValueInt var instance ConversionToPHPValue Int32 where convTo' var = PHPSessionValueInt (fromIntegral var) instance ConversionToPHPValue Int64 where convTo' var = PHPSessionValueInt (fromIntegral var) instance ConversionToPHPValue a => ConversionToPHPValue (Maybe a) where convTo' Nothing = PHPSessionValueNull convTo' (Just var) = convTo' var instance ConversionToPHPValue (PHPSessionClassName, [(PHPSessionValue,PHPSessionValue)]) where convTo' (cls,arr) = PHPSessionValueObject cls arr instance ConversionToPHPValue (PHPSessionClassName, LBS.ByteString) where convTo' (cls,arr) = PHPSessionValueObjectSerializeable cls arr instance ConversionToPHPValue LBS.ByteString where convTo' var = PHPSessionValueString var instance ConversionToPHPValue BS.ByteString where convTo' var = PHPSessionValueString (LBS.fromChunks [var]) instance ConversionFromPHPValueOrMismatch [(PHPSessionValue,PHPSessionValue)] where convFromOM (PHPSessionValueArray var) = Right var convFromOM v = mismatchError v "[(PHPSessionValue,PHPSessionValue)]" instance ConversionFromPHPValueOrMismatch a => ConversionFromPHPValueOrMismatch [(a,PHPSessionValue)] where convFromOM (PHPSessionValueArray vars) = case L.foldl' ontoTuple (Right []) vars of Left str -> Left str Right lst -> Right $ reverse lst where ontoTuple (Left str) _ = Left str ontoTuple (Right lst) (l,r) = let l' = convFromOM l in case l' of Left errl -> Left errl Right l'' -> Right ((l'',r) : lst) convFromOM v = mismatchError v "ConversionFromPHPValueOrMismatch a => ConversionFromPHPValueOrMismatch [(a,PHPSessionValue)]" instance ConversionFromPHPValueOrMismatch b => ConversionFromPHPValueOrMismatch [(PHPSessionValue,b)] where convFromOM (PHPSessionValueArray vars) = convArrayRightSide vars convFromOM convFromOM v = mismatchError v "ConversionFromPHPValueOrMismatch b => ConversionFromPHPValueOrMismatch [(PHPSessionValue,b)]" convArrayRightSide vars conv = case L.foldl' ontoTuple (Right []) vars of Left str -> Left str Right lst -> Right $ reverse lst where ontoTuple (Left str) _ = Left str ontoTuple (Right lst) (l,r) = case conv r of Left errr -> Left errr Right r'' -> Right $ (l, r'') : lst instance (ConversionFromPHPValueOrMismatch a, ConversionFromPHPValueOrMismatch b) => ConversionFromPHPValueOrMismatch [(a,b)] where convFromOM (PHPSessionValueArray vars) = convArrayBothSides vars convFromOM convFromOM v = mismatchError v "(ConversionFromPHPValueOrMismatch a, ConversionFromPHPValueOrMismatch b) => ConversionFromPHPValueOrMismatch [(a,b)]" convArrayBothSides vars conv = case L.foldl' ontoTuple (Right []) vars of Left str -> Left str Right lst -> Right $ reverse lst where ontoTuple (Left str) _ = Left str ontoTuple (Right lst) (l,r) = let l' = convFromOM l r' = conv r in case l' of Left errl -> Left errl Right l'' -> case r' of Left errr -> Left errr Right r'' -> Right ((l'', r'') : lst) instance ConversionFromPHPValueOrMismatch Bool where convFromOM (PHPSessionValueBool var) = Right var convFromOM v = mismatchError v "Bool" instance ConversionFromPHPValueOrMismatch Double where convFromOM (PHPSessionValueFloat (Left var)) = Right $ fromIntegral var convFromOM (PHPSessionValueFloat (Right var)) = Right var convFromOM v = mismatchError v "Double" instance ConversionFromPHPValueOrMismatch Int where convFromOM (PHPSessionValueInt var) = Right var convFromOM v = mismatchError v "Int" instance ConversionFromPHPValueOrMismatch Int32 where convFromOM (PHPSessionValueInt var) = Right $ fromIntegral var convFromOM v = mismatchError v "Int32" instance ConversionFromPHPValueOrMismatch Int64 where convFromOM (PHPSessionValueInt var) = Right $ fromIntegral var convFromOM v = mismatchError v "Int64" instance Integral n => ConversionFromPHPValueOrMismatch (Either n Double) where convFromOM (PHPSessionValueFloat (Right var)) = (Right . Right) var convFromOM (PHPSessionValueFloat (Left var)) = (Right . Right . fromIntegral) var convFromOM (PHPSessionValueInt var) = (Right . Left . fromIntegral) var convFromOM v = mismatchError v "Integral n => Either n Double" instance Integral n => ConversionFromPHPValueOrMismatch (Either n LBS.ByteString) where convFromOM (PHPSessionValueString var) = (Right . Right) var convFromOM (PHPSessionValueInt var) = (Right . Left . fromIntegral) var convFromOM v = mismatchError v "Integral n => Either n ByteString" instance ConversionFromPHPValueOrMismatch (Either Double LBS.ByteString) where convFromOM (PHPSessionValueString var) = (Right . Right) var convFromOM (PHPSessionValueFloat (Right var)) = (Right . Left ) var convFromOM (PHPSessionValueFloat (Left var)) = (Right . Left . fromIntegral) var convFromOM v = mismatchError v "Either Double ByteString" instance Integral n => ConversionFromPHPValueOrMismatch (Either n BS.ByteString) where convFromOM (PHPSessionValueString var) = (Right . Right . BS.concat . LBS.toChunks) var convFromOM (PHPSessionValueInt var) = (Right . Left . fromIntegral) var convFromOM v = mismatchError v "Integral n => Either n ByteString" instance ConversionFromPHPValueOrMismatch (Either Double BS.ByteString) where convFromOM (PHPSessionValueString var) = (Right . Right . BS.concat . LBS.toChunks) var convFromOM (PHPSessionValueFloat (Right var)) = (Right . Left ) var convFromOM (PHPSessionValueFloat (Left var)) = (Right . Left . fromIntegral) var convFromOM v = mismatchError v "Either Double ByteString" instance ConversionFromPHPValueOrMismatch (PHPSessionClassName, [(PHPSessionValue,PHPSessionValue)]) where convFromOM (PHPSessionValueObject cls arr) = Right (cls,arr) convFromOM v = mismatchError v "(PHPSessionClassName, [(PHPSessionValue,PHPSessionValue)])" instance ConversionFromPHPValueOrMismatch (PHPSessionClassName, LBS.ByteString) where convFromOM (PHPSessionValueObjectSerializeable cls arr) = Right (cls,arr) convFromOM v = mismatchError v "(PHPSessionClassName, ByteString)" instance ConversionFromPHPValueOrMismatch LBS.ByteString where convFromOM (PHPSessionValueString var) = Right var convFromOM v = mismatchError v "ByteString" instance ConversionFromPHPValueOrMismatch BS.ByteString where convFromOM (PHPSessionValueString var) = (Right . BS.concat . LBS.toChunks) var convFromOM v = mismatchError v "ByteString" instance ConversionFromPHPValueOrMismatch a => ConversionFromPHPValueOrMismatch (Maybe a) where convFromOM PHPSessionValueNull = Right Nothing convFromOM v = case convFromOM v of Left s -> Left s Right b' -> Right (Just b') mismatchError v totype = Left $ "Type mismatch converting from (" ++ show v ++ ") to " ++ totype -- | 'convFrom' and 'convFromSafe' are convenience functions that translate PHP -- values stored as 'PHPSessionValue' into appropriate Haskell types depending on -- the desired type cast or inferred. Functions provided in this module provide -- non-coerced type translations and so will either carry on the translation or -- signal the fact that the attempted conversion will alter the type of the -- value. For situations where altering value types is expected, alternative -- conversion functions with similar type signatures are provided in modules -- within /Data.PHPSession.ImplicitConv/. -- -- The example arrayOfPHPStrings definition given in 'convTo' can be reverted back -- to Haskell types, which evaluates to @[(0,\"Hello\"),(1,\"World\")]@. -- -- >>> convFrom arrayOfPHPStrings :: [(Int,LBS.ByteString)] -- [(0,"Hello"),(1,"World")] -- -- However, if the desired type signature is changed to a completely different type, -- then a runtime exception is thrown: -- -- >>> convFrom arrayOfPHPStrings :: [(Int,Int)] -- *** Exception: Type mismatch converting from (PHPSessionValueString "Hello") to Int -- -- Where there is the possibility that the value being sought may be @/NULL/@, the -- type should be @('Maybe' a)@. -- convFrom :: ConversionFromPHPValueOrMismatch b => PHPSessionValue -> b convFrom var = case convFromOM var of Left message -> error message Right var' -> var' -- | A version of 'convFrom' which returns a 'Left' with an error message instead -- of throwing an exception. -- convFromSafe :: ConversionFromPHPValueOrMismatch b => PHPSessionValue -> Either String b convFromSafe var = convFromOM var