module Data.Convertible.Instances.String
( InvalidDayException (..)
, InvalidBoolException (..)
#if TEST
, testSuite
#endif
) where
import Data.Convertible.Base
import Data.Typeable (Typeable)
import Control.Exception (Exception)
import Data.Convertible.Instances.Text ()
import Data.Attempt
import Control.Monad ((<=<), unless)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as ST
import qualified Data.Text.Lazy as LT
import Data.Time.Calendar
import Data.Ratio
#if TEST
import Test.Framework (testGroup, Test)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Data.Char (isDigit)
#endif
$(deriveAttempts
[ (''Bool, ''BL.ByteString)
, (''Bool, ''BS.ByteString)
, (''Bool, ''String)
, (''Bool, ''LT.Text)
, (''Bool, ''ST.Text)
, (''Day, ''BL.ByteString)
, (''Day, ''BS.ByteString)
, (''Day, ''String)
, (''Day, ''LT.Text)
, (''Day, ''ST.Text)
, (''Int, ''BL.ByteString)
, (''Int, ''BS.ByteString)
, (''Int, ''String)
, (''Int, ''LT.Text)
, (''Int, ''ST.Text)
, (''Rational, ''BL.ByteString)
, (''Rational, ''BS.ByteString)
, (''Rational, ''String)
, (''Rational, ''LT.Text)
, (''Rational, ''ST.Text)
])
fromStringA :: ConvertAttempt String a => String -> Attempt a
fromStringA = convertAttempt
toString :: ConvertSuccess a String => a -> String
toString = convertSuccess
data InvalidDayException = InvalidDayException String
deriving (Show, Typeable)
instance Exception InvalidDayException
instance ConvertSuccess Day [Char] where
convertSuccess = show
instance ConvertAttempt [Char] Day where
convertAttempt s = wrapFailure (const $ InvalidDayException s) $ do
unless (length s == 10) $ failure $ InvalidDayException s
y <- ca $ take 4 s
m <- ca $ take 2 $ drop 5 s
d <- ca $ take 2 $ drop 8 s
return $ fromGregorian y m d
data InvalidBoolException = InvalidBoolException String
deriving (Show, Typeable)
instance Exception InvalidBoolException
instance ConvertSuccess Bool [Char] where
convertSuccess b = if b then "true" else "false"
instance ConvertAttempt [Char] Bool where
convertAttempt s =
case s of
"y" -> return True
"Y" -> return True
"yes" -> return True
"Yes" -> return True
"YES" -> return True
"true" -> return True
"True" -> return True
"TRUE" -> return True
"on" -> return True
"On" -> return True
"ON" -> return True
"n" -> return False
"N" -> return False
"no" -> return False
"No" -> return False
"NO" -> return False
"false" -> return False
"False" -> return False
"FALSE" -> return False
"off" -> return False
"Off" -> return False
"OFF" -> return False
_ -> failure $ InvalidBoolException s
instance ConvertSuccess Int [Char] where
convertSuccess = show
instance ConvertAttempt [Char] Int where
convertAttempt = readMsg "Invalid Int"
instance ConvertSuccess Integer [Char] where
convertSuccess = show
instance ConvertAttempt [Char] Integer where
convertAttempt = readMsg "Invalid Int"
instance ConvertSuccess Rational [Char] where
convertSuccess r
| denominator r == 1 = show $ numerator r
| otherwise = show $ (fromRational r :: Double)
instance ConvertAttempt [Char] Rational where
convertAttempt = fmap realToFrac . readDouble
readDouble :: String -> Attempt Double
readDouble = readMsg "Invalid double"
readMsg :: Read a => String -> String -> Attempt a
readMsg msg s = case reads s of
(x, _):_ -> Success x
_ -> failureString $ msg ++ ": " ++ s
#if TEST
propRationalId :: Rational -> Bool
propRationalId r = convertAttempt (convertSuccess r :: String) `almostEquals` r
almostEquals :: Attempt Rational -> Rational -> Bool
almostEquals (Failure _) _ = False
almostEquals (Success x) y = abs (x y) < 0.0001
propRationalAllDigits :: Rational -> Bool
propRationalAllDigits = all (\c -> isDigit c || c `elem` ".-e")
. convertSuccess
#endif
instance ConvertAttempt BS.ByteString Day where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt BL.ByteString Day where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt ST.Text Day where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt LT.Text Day where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt BS.ByteString Bool where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt BL.ByteString Bool where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt ST.Text Bool where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt LT.Text Bool where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt BS.ByteString Int where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt BL.ByteString Int where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt ST.Text Int where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt LT.Text Int where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt BS.ByteString Rational where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt BL.ByteString Rational where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt ST.Text Rational where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertAttempt LT.Text Rational where
convertAttempt = fromStringA <=< convertAttempt
instance ConvertSuccess Day BS.ByteString where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Day BL.ByteString where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Day ST.Text where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Day LT.Text where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Bool BS.ByteString where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Bool BL.ByteString where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Bool ST.Text where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Bool LT.Text where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Int BS.ByteString where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Int BL.ByteString where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Int ST.Text where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Int LT.Text where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Rational BS.ByteString where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Rational BL.ByteString where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Rational ST.Text where
convertSuccess = convertSuccess . toString
instance ConvertSuccess Rational LT.Text where
convertSuccess = convertSuccess . toString
#if TEST
testSuite :: Test
testSuite = testGroup "Data.Convertible.Instances.String"
[ testProperty "propRationalId" propRationalId
, testProperty "propRationalAllDigits" propRationalAllDigits
, testProperty "propRationalAllNums" propRationalAllNums
]
propRationalAllNums :: Integer -> Bool
propRationalAllNums i = go (i % 1) where
go :: Rational -> Bool
go r
| r < 0 = go $ negate r
| otherwise = all isDigit $ cs r
#endif