{-# LANGUAGE OverloadedStrings #-}

{-|

The Read type class is very useful for building data types from String
representations.  But String has high overhead, so sometimes it isn't suitable
for applications where space usage and performance are important.  This
library provides a simpler version of Read's functionality for Text and
ByteStrings.

-}

module Data.Readable
  ( Readable(..)
  ) where

------------------------------------------------------------------------------
import           Control.Monad
import           Data.ByteString.Char8 (ByteString)
import           Data.Int
import           Data.Text (Text)
import qualified Data.Text as T
import           Data.Text.Encoding
import           Data.Text.Read
import           Data.Word


------------------------------------------------------------------------------
-- | ByteString and Text reading using MonadPlus to handle parse failure.  On
-- error, fromText and fromBS will return mzero.  You can use mplus to provide
-- fallback defaults.
class Readable a where
    -- | Reads data from a Text representation.
    fromText :: MonadPlus m => Text -> m a
    -- | Reads data from a UTF8 encoded ByteString.  The default
    -- implementation of this function simply decodes with UTF-8 and then
    -- calls the fromText function.  If decoding fails, mzero will be
    -- returned.  You can provide your own implementation if you need
    -- different behavior such as not decoding to UTF8.
    fromBS   :: MonadPlus m => ByteString -> m a
    fromBS = Text -> m a
forall a (m :: * -> *). (Readable a, MonadPlus m) => Text -> m a
fromText (Text -> m a) -> (ByteString -> m Text) -> ByteString -> m a
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< Either UnicodeException Text -> m Text
forall (m :: * -> *) a b. MonadPlus m => Either a b -> m b
hushPlus (Either UnicodeException Text -> m Text)
-> (ByteString -> Either UnicodeException Text)
-> ByteString
-> m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either UnicodeException Text
decodeUtf8'


hushPlus :: MonadPlus m => Either a b -> m b
hushPlus :: Either a b -> m b
hushPlus (Left _) = m b
forall (m :: * -> *) a. MonadPlus m => m a
mzero
hushPlus (Right b :: b
b) = b -> m b
forall (m :: * -> *) a. Monad m => a -> m a
return b
b


------------------------------------------------------------------------------
-- | Fails if the input wasn't parsed completely.
checkComplete :: MonadPlus m => (t, Text) -> m t
checkComplete :: (t, Text) -> m t
checkComplete (a :: t
a,rest :: Text
rest)
  | Text -> Bool
T.null Text
rest = t -> m t
forall (m :: * -> *) a. Monad m => a -> m a
return t
a
  | Bool
otherwise   = m t
forall (m :: * -> *) a. MonadPlus m => m a
mzero


-- Leaving out these instances breaks users who depend on having a unified
-- constraint for parsing, so we need to keep them around.
instance Readable ByteString where
    fromText :: Text -> m ByteString
fromText = ByteString -> m ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> m ByteString)
-> (Text -> ByteString) -> Text -> m ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8
    fromBS :: ByteString -> m ByteString
fromBS = ByteString -> m ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return
instance Readable Text where
    fromText :: Text -> m Text
fromText = Text -> m Text
forall (m :: * -> *) a. Monad m => a -> m a
return

instance Readable Int where
    fromText :: Text -> m Int
fromText = (String -> m Int)
-> ((Int, Text) -> m Int) -> Either String (Int, Text) -> m Int
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Int -> String -> m Int
forall a b. a -> b -> a
const m Int
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Int, Text) -> m Int
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Int, Text) -> m Int)
-> (Text -> Either String (Int, Text)) -> Text -> m Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Either String (Int, Text))
-> Text -> Either String (Int, Text)
forall a. Num a => Reader a -> Reader a
signed Text -> Either String (Int, Text)
forall a. Integral a => Reader a
decimal
instance Readable Integer where
    fromText :: Text -> m Integer
fromText = (String -> m Integer)
-> ((Integer, Text) -> m Integer)
-> Either String (Integer, Text)
-> m Integer
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Integer -> String -> m Integer
forall a b. a -> b -> a
const m Integer
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Integer, Text) -> m Integer
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Integer, Text) -> m Integer)
-> (Text -> Either String (Integer, Text)) -> Text -> m Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Either String (Integer, Text))
-> Text -> Either String (Integer, Text)
forall a. Num a => Reader a -> Reader a
signed Text -> Either String (Integer, Text)
forall a. Integral a => Reader a
decimal
instance Readable Float where
    fromText :: Text -> m Float
fromText = (String -> m Float)
-> ((Float, Text) -> m Float)
-> Either String (Float, Text)
-> m Float
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Float -> String -> m Float
forall a b. a -> b -> a
const m Float
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Float, Text) -> m Float
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Float, Text) -> m Float)
-> (Text -> Either String (Float, Text)) -> Text -> m Float
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String (Float, Text)
forall a. Fractional a => Reader a
rational
instance Readable Double where
    fromText :: Text -> m Double
fromText = (String -> m Double)
-> ((Double, Text) -> m Double)
-> Either String (Double, Text)
-> m Double
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Double -> String -> m Double
forall a b. a -> b -> a
const m Double
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Double, Text) -> m Double
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Double, Text) -> m Double)
-> (Text -> Either String (Double, Text)) -> Text -> m Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String (Double, Text)
double
instance Readable Bool where
    fromText :: Text -> m Bool
fromText t :: Text
t = case Text -> Text
T.toLower Text
t of
                   "1" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                   "0" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                   "t" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                   "f" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                   "true" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                   "false" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                   "y" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                   "n" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                   "yes" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                   "no" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                   _ -> m Bool
forall (m :: * -> *) a. MonadPlus m => m a
mzero

instance Readable Int8 where
    fromText :: Text -> m Int8
fromText = (String -> m Int8)
-> ((Int8, Text) -> m Int8) -> Either String (Int8, Text) -> m Int8
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Int8 -> String -> m Int8
forall a b. a -> b -> a
const m Int8
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Int8, Text) -> m Int8
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Int8, Text) -> m Int8)
-> (Text -> Either String (Int8, Text)) -> Text -> m Int8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Either String (Int8, Text))
-> Text -> Either String (Int8, Text)
forall a. Num a => Reader a -> Reader a
signed Text -> Either String (Int8, Text)
forall a. Integral a => Reader a
decimal
instance Readable Int16 where
    fromText :: Text -> m Int16
fromText = (String -> m Int16)
-> ((Int16, Text) -> m Int16)
-> Either String (Int16, Text)
-> m Int16
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Int16 -> String -> m Int16
forall a b. a -> b -> a
const m Int16
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Int16, Text) -> m Int16
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Int16, Text) -> m Int16)
-> (Text -> Either String (Int16, Text)) -> Text -> m Int16
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Either String (Int16, Text))
-> Text -> Either String (Int16, Text)
forall a. Num a => Reader a -> Reader a
signed Text -> Either String (Int16, Text)
forall a. Integral a => Reader a
decimal
instance Readable Int32 where
    fromText :: Text -> m Int32
fromText = (String -> m Int32)
-> ((Int32, Text) -> m Int32)
-> Either String (Int32, Text)
-> m Int32
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Int32 -> String -> m Int32
forall a b. a -> b -> a
const m Int32
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Int32, Text) -> m Int32
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Int32, Text) -> m Int32)
-> (Text -> Either String (Int32, Text)) -> Text -> m Int32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Either String (Int32, Text))
-> Text -> Either String (Int32, Text)
forall a. Num a => Reader a -> Reader a
signed Text -> Either String (Int32, Text)
forall a. Integral a => Reader a
decimal
instance Readable Int64 where
    fromText :: Text -> m Int64
fromText = (String -> m Int64)
-> ((Int64, Text) -> m Int64)
-> Either String (Int64, Text)
-> m Int64
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Int64 -> String -> m Int64
forall a b. a -> b -> a
const m Int64
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Int64, Text) -> m Int64
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Int64, Text) -> m Int64)
-> (Text -> Either String (Int64, Text)) -> Text -> m Int64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Either String (Int64, Text))
-> Text -> Either String (Int64, Text)
forall a. Num a => Reader a -> Reader a
signed Text -> Either String (Int64, Text)
forall a. Integral a => Reader a
decimal

instance Readable Word8 where
    fromText :: Text -> m Word8
fromText = (String -> m Word8)
-> ((Word8, Text) -> m Word8)
-> Either String (Word8, Text)
-> m Word8
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Word8 -> String -> m Word8
forall a b. a -> b -> a
const m Word8
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Word8, Text) -> m Word8
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Word8, Text) -> m Word8)
-> (Text -> Either String (Word8, Text)) -> Text -> m Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String (Word8, Text)
forall a. Integral a => Reader a
decimal
instance Readable Word16 where
    fromText :: Text -> m Word16
fromText = (String -> m Word16)
-> ((Word16, Text) -> m Word16)
-> Either String (Word16, Text)
-> m Word16
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Word16 -> String -> m Word16
forall a b. a -> b -> a
const m Word16
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Word16, Text) -> m Word16
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Word16, Text) -> m Word16)
-> (Text -> Either String (Word16, Text)) -> Text -> m Word16
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String (Word16, Text)
forall a. Integral a => Reader a
decimal
instance Readable Word32 where
    fromText :: Text -> m Word32
fromText = (String -> m Word32)
-> ((Word32, Text) -> m Word32)
-> Either String (Word32, Text)
-> m Word32
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Word32 -> String -> m Word32
forall a b. a -> b -> a
const m Word32
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Word32, Text) -> m Word32
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Word32, Text) -> m Word32)
-> (Text -> Either String (Word32, Text)) -> Text -> m Word32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String (Word32, Text)
forall a. Integral a => Reader a
decimal
instance Readable Word64 where
    fromText :: Text -> m Word64
fromText = (String -> m Word64)
-> ((Word64, Text) -> m Word64)
-> Either String (Word64, Text)
-> m Word64
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m Word64 -> String -> m Word64
forall a b. a -> b -> a
const m Word64
forall (m :: * -> *) a. MonadPlus m => m a
mzero) (Word64, Text) -> m Word64
forall (m :: * -> *) t. MonadPlus m => (t, Text) -> m t
checkComplete (Either String (Word64, Text) -> m Word64)
-> (Text -> Either String (Word64, Text)) -> Text -> m Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String (Word64, Text)
forall a. Integral a => Reader a
decimal