csv-conduit-0.7.3.0: src/Data/CSV/Conduit/Conversion.hs
{-# LANGUAGE BangPatterns, CPP, FlexibleInstances, OverloadedStrings,
Rank2Types #-}
#ifdef GENERICS
{-# LANGUAGE DefaultSignatures, TypeOperators, KindSignatures, FlexibleContexts,
MultiParamTypeClasses, UndecidableInstances, ScopedTypeVariables,
DataKinds #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Module : Data.CSV.Conduit.Conversion
-- Copyright : Ozgun Ataman, Johan Tibell
-- License : BSD3
--
-- Maintainer : Ozgun Ataman <ozataman@gmail.com>
-- Stability : experimental
--
-- This module has been shamelessly taken from Johan Tibell's nicely
-- put together cassava package, which itself borrows the approach
-- from Bryan O'Sullivan's widely used aeson package.
--
-- We make the necessary adjustments and some simplifications here to
-- bolt this parsing interface onto our underlying "CSV" typeclass.
----------------------------------------------------------------------------
module Data.CSV.Conduit.Conversion
(
-- * Type conversion
Only(..)
, Named (..)
, NamedOrdered (..)
, Record
, NamedRecord
, NamedRecordOrdered
, FromRecord(..)
, FromNamedRecord(..)
, FromNamedRecordOrdered(..)
, ToNamedRecord(..)
, ToNamedRecordOrdered(..)
, FromField(..)
, ToRecord(..)
, ToField(..)
, Field
-- * Parser
, Parser
, runParser
-- * Accessors
, index
, (.!)
, unsafeIndex
, lookup
, lookupOrdered
, (.:)
, namedField
, (.=)
, record
, namedRecord
, namedRecordOrdered
) where
import Control.Applicative as A
import Control.Monad (MonadPlus, mplus, mzero)
import Data.Attoparsec.ByteString.Char8 (double, parseOnly)
import qualified Data.Attoparsec.ByteString.Char8 as A8
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as L
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Kind (Type)
import qualified Data.Map as M
import qualified Data.Map.Ordered as MO
import Data.Semigroup as Semigroup
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.Encoding as LT
import Data.Traversable as DT
import Data.Vector (Vector, (!))
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import Data.Word as W
import GHC.Float (double2Float)
import Prelude hiding (lookup, takeWhile)
#ifdef GENERICS
import GHC.Generics
import qualified Data.IntMap as IM
#endif
import Data.CSV.Conduit.Conversion.Internal
------------------------------------------------------------------------
-- bytestring compatibility
toStrict :: L.ByteString -> B.ByteString
fromStrict :: B.ByteString -> L.ByteString
#if MIN_VERSION_bytestring(0,10,0)
toStrict = L.toStrict
fromStrict = L.fromStrict
#else
toStrict = B.concat . L.toChunks
fromStrict = L.fromChunks . (:[])
#endif
{-# INLINE toStrict #-}
{-# INLINE fromStrict #-}
------------------------------------------------------------------------
-- Type conversion
-- | A shorthand for the ByteString case of 'MapRow'
type NamedRecord = M.Map B8.ByteString B8.ByteString
type NamedRecordOrdered = MO.OMap B8.ByteString B8.ByteString
-- | A wrapper around custom haskell types that can directly be
-- converted/parsed from an incoming CSV stream.
--
-- We define this wrapper to stop GHC from complaining
-- about overlapping instances. Just use 'getNamed' to get your
-- object out of the wrapper.
newtype Named a = Named { getNamed :: a } deriving (Eq,Show,Read,Ord)
newtype NamedOrdered a = NamedOrdered { getNamedOrdered :: a } deriving (Eq,Show,Read,Ord)
-- | A record corresponds to a single line in a CSV file.
type Record = Vector B8.ByteString
-- | A single field within a record.
type Field = B8.ByteString
------------------------------------------------------------------------
-- Index-based conversion
-- | A type that can be converted from a single CSV record, with the
-- possibility of failure.
--
-- When writing an instance, use 'empty', 'mzero', or 'fail' to make a
-- conversion fail, e.g. if a 'Record' has the wrong number of
-- columns.
--
-- Given this example data:
--
-- > John,56
-- > Jane,55
--
-- here's an example type and instance:
--
-- > data Person = Person { name :: !Text, age :: !Int }
-- >
-- > instance FromRecord Person where
-- > parseRecord v
-- > | length v == 2 = Person <$>
-- > v .! 0 <*>
-- > v .! 1
-- > | otherwise = mzero
class FromRecord a where
parseRecord :: Record -> Parser a
#ifdef GENERICS
default parseRecord :: (Generic a, GFromRecord (Rep a)) => Record -> Parser a
parseRecord r = to A.<$> gparseRecord r
#endif
-- | Haskell lacks a single-element tuple type, so if you CSV data
-- with just one column you can use the 'Only' type to represent a
-- single-column result.
newtype Only a = Only {
fromOnly :: a
} deriving (Eq, Ord, Read, Show)
-- | A type that can be converted to a single CSV record.
--
-- An example type and instance:
--
-- > data Person = Person { name :: !Text, age :: !Int }
-- >
-- > instance ToRecord Person where
-- > toRecord (Person name age) = record [
-- > toField name, toField age]
--
-- Outputs data on this form:
--
-- > John,56
-- > Jane,55
class ToRecord a where
toRecord :: a -> Record
#ifdef GENERICS
default toRecord :: (Generic a, GToRecord (Rep a) Field) => a -> Record
toRecord = V.fromList . gtoRecord . from
#endif
instance FromField a => FromRecord (Only a) where
parseRecord v
| n == 1 = Only <$> unsafeIndex v 0
| otherwise = lengthMismatch 1 v
where
n = V.length v
-- TODO: Check if we want all toRecord conversions to be stricter.
instance ToField a => ToRecord (Only a) where
toRecord = V.singleton . toField . fromOnly
instance (FromField a, FromField b) => FromRecord (a, b) where
parseRecord v
| n == 2 = (,) <$> unsafeIndex v 0
<*> unsafeIndex v 1
| otherwise = lengthMismatch 2 v
where
n = V.length v
instance (ToField a, ToField b) => ToRecord (a, b) where
toRecord (a, b) = V.fromList [toField a, toField b]
instance (FromField a, FromField b, FromField c) => FromRecord (a, b, c) where
parseRecord v
| n == 3 = (,,) <$> unsafeIndex v 0
<*> unsafeIndex v 1
<*> unsafeIndex v 2
| otherwise = lengthMismatch 3 v
where
n = V.length v
instance (ToField a, ToField b, ToField c) =>
ToRecord (a, b, c) where
toRecord (a, b, c) = V.fromList [toField a, toField b, toField c]
instance (FromField a, FromField b, FromField c, FromField d) =>
FromRecord (a, b, c, d) where
parseRecord v
| n == 4 = (,,,) <$> unsafeIndex v 0
<*> unsafeIndex v 1
<*> unsafeIndex v 2
<*> unsafeIndex v 3
| otherwise = lengthMismatch 4 v
where
n = V.length v
instance (ToField a, ToField b, ToField c, ToField d) =>
ToRecord (a, b, c, d) where
toRecord (a, b, c, d) = V.fromList [
toField a, toField b, toField c, toField d]
instance (FromField a, FromField b, FromField c, FromField d, FromField e) =>
FromRecord (a, b, c, d, e) where
parseRecord v
| n == 5 = (,,,,) <$> unsafeIndex v 0
<*> unsafeIndex v 1
<*> unsafeIndex v 2
<*> unsafeIndex v 3
<*> unsafeIndex v 4
| otherwise = lengthMismatch 5 v
where
n = V.length v
instance (ToField a, ToField b, ToField c, ToField d, ToField e) =>
ToRecord (a, b, c, d, e) where
toRecord (a, b, c, d, e) = V.fromList [
toField a, toField b, toField c, toField d, toField e]
instance (FromField a, FromField b, FromField c, FromField d, FromField e,
FromField f) =>
FromRecord (a, b, c, d, e, f) where
parseRecord v
| n == 6 = (,,,,,) <$> unsafeIndex v 0
<*> unsafeIndex v 1
<*> unsafeIndex v 2
<*> unsafeIndex v 3
<*> unsafeIndex v 4
<*> unsafeIndex v 5
| otherwise = lengthMismatch 6 v
where
n = V.length v
instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f) =>
ToRecord (a, b, c, d, e, f) where
toRecord (a, b, c, d, e, f) = V.fromList [
toField a, toField b, toField c, toField d, toField e, toField f]
instance (FromField a, FromField b, FromField c, FromField d, FromField e,
FromField f, FromField g) =>
FromRecord (a, b, c, d, e, f, g) where
parseRecord v
| n == 7 = (,,,,,,) <$> unsafeIndex v 0
<*> unsafeIndex v 1
<*> unsafeIndex v 2
<*> unsafeIndex v 3
<*> unsafeIndex v 4
<*> unsafeIndex v 5
<*> unsafeIndex v 6
| otherwise = lengthMismatch 7 v
where
n = V.length v
instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
ToField g) =>
ToRecord (a, b, c, d, e, f, g) where
toRecord (a, b, c, d, e, f, g) = V.fromList [
toField a, toField b, toField c, toField d, toField e, toField f,
toField g]
lengthMismatch :: Int -> Record -> Parser a
lengthMismatch expected v =
fail $ "cannot unpack array of length " ++
show n ++ " into a " ++ desired ++ ". Input record: " ++
show v
where
n = V.length v
desired | expected == 1 = "Only"
| expected == 2 = "pair"
| otherwise = show expected ++ "-tuple"
instance FromField a => FromRecord [a] where
parseRecord = DT.traverse parseField . V.toList
instance ToField a => ToRecord [a] where
toRecord = V.fromList . map toField
instance FromField a => FromRecord (V.Vector a) where
parseRecord = traverse parseField
instance ToField a => ToRecord (Vector a) where
toRecord = V.map toField
instance (FromField a, U.Unbox a) => FromRecord (U.Vector a) where
parseRecord = fmap U.convert . traverse parseField
instance (ToField a, U.Unbox a) => ToRecord (U.Vector a) where
toRecord = V.map toField . U.convert
------------------------------------------------------------------------
-- Name-based conversion
-- | A type that can be converted from a single CSV record, with the
-- possibility of failure.
--
-- When writing an instance, use 'empty', 'mzero', or 'fail' to make a
-- conversion fail, e.g. if a 'Record' has the wrong number of
-- columns.
--
-- Given this example data:
--
-- > name,age
-- > John,56
-- > Jane,55
--
-- here's an example type and instance:
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- >
-- > data Person = Person { name :: !Text, age :: !Int }
-- >
-- > instance FromNamedRecord Person where
-- > parseNamedRecord m = Person <$>
-- > m .: "name" <*>
-- > m .: "age"
--
-- Note the use of the @OverloadedStrings@ language extension which
-- enables 'B8.ByteString' values to be written as string literals.
class FromNamedRecord a where
parseNamedRecord :: NamedRecord -> Parser a
#ifdef GENERICS
default parseNamedRecord :: (Generic a, GFromNamedRecord (Rep a)) => NamedRecord -> Parser a
parseNamedRecord r = to <$> gparseNamedRecord r
#endif
class FromNamedRecordOrdered a where
parseNamedRecordOrdered :: NamedRecordOrdered -> Parser a
-- | A type that can be converted to a single CSV record.
--
-- An example type and instance:
--
-- > data Person = Person { name :: !Text, age :: !Int }
-- >
-- > instance ToNamedRecord Person where
-- > toNamedRecord (Person name age) = namedRecord [
-- > "name" .= name, "age" .= age]
class ToNamedRecord a where
toNamedRecord :: a -> NamedRecord
#ifdef GENERICS
default toNamedRecord :: (Generic a, GToRecord (Rep a) (B.ByteString, B.ByteString)) => a -> NamedRecord
toNamedRecord = namedRecord . gtoRecord . from
#endif
class ToNamedRecordOrdered a where
toNamedRecordOrdered :: a -> NamedRecordOrdered
instance FromField a => FromNamedRecord (M.Map B.ByteString a) where
parseNamedRecord m = traverse parseField m
instance FromField a => FromNamedRecordOrdered (MO.OMap B.ByteString a) where
parseNamedRecordOrdered m = traverse parseField m
instance ToField a => ToNamedRecord (M.Map B.ByteString a) where
toNamedRecord = M.map toField
instance ToField a => ToNamedRecordOrdered (MO.OMap B.ByteString a) where
toNamedRecordOrdered a = MO.fromList $ map (fmap toField) $ MO.assocs a
-- instance FromField a => FromNamedRecord (HM.HashMap B.ByteString a) where
-- parseNamedRecord m = traverse (\ s -> parseField s) m
-- instance ToField a => ToNamedRecord (HM.HashMap B.ByteString a) where
-- toNamedRecord = HM.map toField
------------------------------------------------------------------------
-- Individual field conversion
-- | A type that can be converted from a single CSV field, with the
-- possibility of failure.
--
-- When writing an instance, use 'empty', 'mzero', or 'fail' to make a
-- conversion fail, e.g. if a 'Field' can't be converted to the given
-- type.
--
-- Example type and instance:
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- >
-- > data Color = Red | Green | Blue
-- >
-- > instance FromField Color where
-- > parseField s
-- > | s == "R" = pure Red
-- > | s == "G" = pure Green
-- > | s == "B" = pure Blue
-- > | otherwise = mzero
class FromField a where
parseField :: Field -> Parser a
-- | A type that can be converted to a single CSV field.
--
-- Example type and instance:
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- >
-- > data Color = Red | Green | Blue
-- >
-- > instance ToField Color where
-- > toField Red = "R"
-- > toField Green = "G"
-- > toField Blue = "B"
class ToField a where
toField :: a -> Field
-- | 'Nothing' if the 'Field' is 'B.empty', 'Just' otherwise.
instance FromField a => FromField (Maybe a) where
parseField s
| B.null s = pure Nothing
| otherwise = Just <$> parseField s
{-# INLINE parseField #-}
-- | 'Nothing' is encoded as an 'B.empty' field.
instance ToField a => ToField (Maybe a) where
toField = maybe B.empty toField
{-# INLINE toField #-}
-- | Ignores the 'Field'. Always succeeds.
instance FromField () where
parseField _ = pure ()
{-# INLINE parseField #-}
-- | Assumes UTF-8 encoding.
instance FromField Char where
parseField s =
case T.decodeUtf8' s of
Left e -> fail $ show e
Right t
| T.compareLength t 1 == EQ -> pure (T.head t)
| otherwise -> typeError "Char" s Nothing
{-# INLINE parseField #-}
-- | Uses UTF-8 encoding.
instance ToField Char where
toField = toField . T.encodeUtf8 . T.singleton
{-# INLINE toField #-}
-- | Accepts same syntax as 'rational'.
instance FromField Double where
parseField = parseDouble
{-# INLINE parseField #-}
-- | Uses decimal notation or scientific notation, depending on the
-- number.
instance ToField Double where
toField = realFloat
{-# INLINE toField #-}
-- | Accepts same syntax as 'rational'.
instance FromField Float where
parseField s = double2Float <$> parseDouble s
{-# INLINE parseField #-}
-- | Uses decimal notation or scientific notation, depending on the
-- number.
instance ToField Float where
toField = realFloat
{-# INLINE toField #-}
parseDouble :: B.ByteString -> Parser Double
parseDouble s = case parseOnly double s of
Left err -> typeError "Double" s (Just err)
Right n -> pure n
{-# INLINE parseDouble #-}
-- | Accepts a signed decimal number.
instance FromField Int where
parseField = parseSigned "Int"
{-# INLINE parseField #-}
-- | Uses decimal encoding with optional sign.
instance ToField Int where
toField = decimal
{-# INLINE toField #-}
-- | Accepts a signed decimal number.
instance FromField Integer where
parseField = parseSigned "Integer"
{-# INLINE parseField #-}
-- | Uses decimal encoding with optional sign.
instance ToField Integer where
toField = decimal
{-# INLINE toField #-}
-- | Accepts a signed decimal number.
instance FromField Int8 where
parseField = parseSigned "Int8"
{-# INLINE parseField #-}
-- | Uses decimal encoding with optional sign.
instance ToField Int8 where
toField = decimal
{-# INLINE toField #-}
-- | Accepts a signed decimal number.
instance FromField Int16 where
parseField = parseSigned "Int16"
{-# INLINE parseField #-}
-- | Uses decimal encoding with optional sign.
instance ToField Int16 where
toField = decimal
{-# INLINE toField #-}
-- | Accepts a signed decimal number.
instance FromField Int32 where
parseField = parseSigned "Int32"
{-# INLINE parseField #-}
-- | Uses decimal encoding with optional sign.
instance ToField Int32 where
toField = decimal
{-# INLINE toField #-}
-- | Accepts a signed decimal number.
instance FromField Int64 where
parseField = parseSigned "Int64"
{-# INLINE parseField #-}
-- | Uses decimal encoding with optional sign.
instance ToField Int64 where
toField = decimal
{-# INLINE toField #-}
-- | Accepts an unsigned decimal number.
instance FromField W.Word where
parseField = parseUnsigned "Word"
{-# INLINE parseField #-}
-- | Uses decimal encoding.
instance ToField Word where
toField = decimal
{-# INLINE toField #-}
-- | Accepts an unsigned decimal number.
instance FromField Word8 where
parseField = parseUnsigned "Word8"
{-# INLINE parseField #-}
-- | Uses decimal encoding.
instance ToField Word8 where
toField = decimal
{-# INLINE toField #-}
-- | Accepts an unsigned decimal number.
instance FromField Word16 where
parseField = parseUnsigned "Word16"
{-# INLINE parseField #-}
-- | Uses decimal encoding.
instance ToField Word16 where
toField = decimal
{-# INLINE toField #-}
-- | Accepts an unsigned decimal number.
instance FromField Word32 where
parseField = parseUnsigned "Word32"
{-# INLINE parseField #-}
-- | Uses decimal encoding.
instance ToField Word32 where
toField = decimal
{-# INLINE toField #-}
-- | Accepts an unsigned decimal number.
instance FromField Word64 where
parseField = parseUnsigned "Word64"
{-# INLINE parseField #-}
-- | Uses decimal encoding.
instance ToField Word64 where
toField = decimal
{-# INLINE toField #-}
instance FromField B.ByteString where
parseField = pure
{-# INLINE parseField #-}
instance ToField B.ByteString where
toField = id
{-# INLINE toField #-}
instance FromField L.ByteString where
parseField = pure . fromStrict
{-# INLINE parseField #-}
instance ToField L.ByteString where
toField = toStrict
{-# INLINE toField #-}
-- | Assumes UTF-8 encoding. Fails on invalid byte sequences.
instance FromField T.Text where
parseField = either (fail . show) pure . T.decodeUtf8'
{-# INLINE parseField #-}
-- | Uses UTF-8 encoding.
instance ToField T.Text where
toField = toField . T.encodeUtf8
{-# INLINE toField #-}
-- | Assumes UTF-8 encoding. Fails on invalid byte sequences.
instance FromField LT.Text where
parseField = either (fail . show) (pure . LT.fromStrict) . T.decodeUtf8'
{-# INLINE parseField #-}
-- | Uses UTF-8 encoding.
instance ToField LT.Text where
toField = toField . toStrict . LT.encodeUtf8
{-# INLINE toField #-}
-- | Assumes UTF-8 encoding. Fails on invalid byte sequences.
instance FromField [Char] where
parseField = fmap T.unpack . parseField
{-# INLINE parseField #-}
-- | Uses UTF-8 encoding.
instance ToField [Char] where
toField = toField . T.pack
{-# INLINE toField #-}
parseSigned :: (Integral a) => String -> B.ByteString -> Parser a
parseSigned typ s = case parseOnly (A8.signed A8.decimal) s of
Left err -> typeError typ s (Just err)
Right n -> pure n
{-# INLINE parseSigned #-}
parseUnsigned :: Integral a => String -> B.ByteString -> Parser a
parseUnsigned typ s = case parseOnly A8.decimal s of
Left err -> typeError typ s (Just err)
Right n -> pure n
{-# INLINE parseUnsigned #-}
typeError :: String -> B.ByteString -> Maybe String -> Parser a
typeError typ s mmsg =
fail $ "expected " ++ typ ++ ", got " ++ show (B8.unpack s) ++ cause
where
cause = case mmsg of
Just msg -> " (" ++ msg ++ ")"
Nothing -> ""
------------------------------------------------------------------------
-- Constructors and accessors
-- | Retrieve the /n/th field in the given record. The result is
-- 'empty' if the value cannot be converted to the desired type.
-- Raises an exception if the index is out of bounds.
--
-- 'index' is a simple convenience function that is equivalent to
-- @'parseField' (v '!' idx)@. If you're certain that the index is not
-- out of bounds, using 'unsafeIndex' is somewhat faster.
index :: FromField a => Record -> Int -> Parser a
index v idx = parseField (v ! idx)
{-# INLINE index #-}
-- | Alias for 'index'.
(.!) :: FromField a => Record -> Int -> Parser a
(.!) = index
{-# INLINE (.!) #-}
infixl 9 .!
-- | Like 'index' but without bounds checking.
unsafeIndex :: FromField a => Record -> Int -> Parser a
unsafeIndex v idx = parseField (V.unsafeIndex v idx)
{-# INLINE unsafeIndex #-}
-- | Retrieve a field in the given record by name. The result is
-- 'empty' if the field is missing or if the value cannot be converted
-- to the desired type.
lookup :: FromField a => NamedRecord -> B.ByteString -> Parser a
lookup m name = maybe (fail err) parseField $ M.lookup name m
where err = "no field named " ++ show (B8.unpack name)
{-# INLINE lookup #-}
lookupOrdered :: FromField a => NamedRecordOrdered -> B.ByteString -> Parser a
lookupOrdered m name = maybe (fail err) parseField $ MO.lookup name m
where err = "no field named " ++ show (B8.unpack name)
{-# INLINE lookupOrdered #-}
-- | Alias for 'lookup'.
(.:) :: FromField a => NamedRecord -> B.ByteString -> Parser a
(.:) = lookup
{-# INLINE (.:) #-}
-- | Construct a pair from a name and a value. For use with
-- 'namedRecord'.
namedField :: ToField a => B.ByteString -> a -> (B.ByteString, B.ByteString)
namedField name val = (name, toField val)
{-# INLINE namedField #-}
-- | Alias for 'namedField'.
(.=) :: ToField a => B.ByteString -> a -> (B.ByteString, B.ByteString)
(.=) = namedField
{-# INLINE (.=) #-}
-- | Construct a record from a list of 'B.ByteString's. Use 'toField'
-- to convert values to 'B.ByteString's for use with 'record'.
record :: [B.ByteString] -> Record
record = V.fromList
-- | Construct a named record from a list of name-value 'B.ByteString'
-- pairs. Use '.=' to construct such a pair from a name and a value.
namedRecord :: [(B.ByteString, B.ByteString)] -> NamedRecord
namedRecord = M.fromList
namedRecordOrdered :: [(B.ByteString, B.ByteString)] -> NamedRecordOrdered
namedRecordOrdered = MO.fromList
------------------------------------------------------------------------
-- Parser for converting records to data types
-- | Failure continuation.
type Failure f r = String -> f r
-- | Success continuation.
type Success a f r = a -> f r
-- | Conversion of a field to a value might fail e.g. if the field is
-- malformed. This possibility is captured by the 'Parser' type, which
-- lets you compose several field conversions together in such a way
-- that if any of them fail, the whole record conversion fails.
newtype Parser a = Parser {
unParser :: forall f r.
Failure f r
-> Success a f r
-> f r
}
instance Monad Parser where
m >>= g = Parser $ \kf ks -> let ks' a = unParser (g a) kf ks
in unParser m kf ks'
{-# INLINE (>>=) #-}
return a = Parser $ \_kf ks -> ks a
{-# INLINE return #-}
#if MIN_VERSION_base(4,13,0)
instance MonadFail Parser where
#endif
fail msg = Parser $ \kf _ks -> kf msg
{-# INLINE fail #-}
instance Functor Parser where
fmap f m = Parser $ \kf ks -> let ks' a = ks (f a)
in unParser m kf ks'
{-# INLINE fmap #-}
instance Applicative Parser where
pure = return
{-# INLINE pure #-}
(<*>) = apP
{-# INLINE (<*>) #-}
instance Alternative Parser where
empty = fail "empty"
{-# INLINE empty #-}
(<|>) = mplus
{-# INLINE (<|>) #-}
instance MonadPlus Parser where
mzero = fail "mzero"
{-# INLINE mzero #-}
mplus a b = Parser $ \kf ks -> let kf' _ = unParser b kf ks
in unParser a kf' ks
{-# INLINE mplus #-}
instance Semigroup.Semigroup (Parser a) where
(<>) = mplus
{-# INLINE (<>) #-}
apP :: Parser (a -> b) -> Parser a -> Parser b
apP d e = do
b <- d
a <- e
return (b a)
{-# INLINE apP #-}
-- | Run a 'Parser', returning either @'Left' errMsg@ or @'Right'
-- result@. Forces the value in the 'Left' or 'Right' constructors to
-- weak head normal form.
--
-- You most likely won't need to use this function directly, but it's
-- included for completeness.
runParser :: Parser a -> Either String a
runParser p = unParser p left right
where
left !errMsg = Left errMsg
right !x = Right x
{-# INLINE runParser #-}
#ifdef GENERICS
class GFromRecord f where
gparseRecord :: Record -> Parser (f p)
instance GFromRecordSum f Record => GFromRecord (M1 i n f) where
gparseRecord v =
case (IM.lookup n gparseRecordSum) of
Nothing -> lengthMismatch n v
Just p -> M1 <$> p v
where
n = V.length v
class GFromNamedRecord f where
gparseNamedRecord :: NamedRecord -> Parser (f p)
instance GFromRecordSum f NamedRecord => GFromNamedRecord (M1 i n f) where
gparseNamedRecord v =
foldr (\f p -> p <|> M1 <$> f v) empty (IM.elems gparseRecordSum)
class GFromRecordSum f r where
gparseRecordSum :: IM.IntMap (r -> Parser (f p))
instance (GFromRecordSum a r, GFromRecordSum b r) => GFromRecordSum (a :+: b) r where
gparseRecordSum =
IM.unionWith (\a b r -> a r <|> b r)
(fmap (L1 <$>) <$> gparseRecordSum)
(fmap (R1 <$>) <$> gparseRecordSum)
instance GFromRecordProd f r => GFromRecordSum (M1 i n f) r where
gparseRecordSum = IM.singleton n (fmap (M1 <$>) f)
where
(n, f) = gparseRecordProd 0
class GFromRecordProd f r where
gparseRecordProd :: Int -> (Int, r -> Parser (f p))
instance GFromRecordProd U1 r where
gparseRecordProd n = (n, const (pure U1))
instance (GFromRecordProd a r, GFromRecordProd b r) => GFromRecordProd (a :*: b) r where
gparseRecordProd n0 = (n2, f)
where
f r = (:*:) <$> fa r <*> fb r
(n1, fa) = gparseRecordProd n0
(n2, fb) = gparseRecordProd n1
instance GFromRecordProd f Record => GFromRecordProd (M1 i n f) Record where
gparseRecordProd n = fmap (M1 <$>) <$> gparseRecordProd n
instance FromField a => GFromRecordProd (K1 i a) Record where
gparseRecordProd n = (n + 1, \v -> K1 <$> parseField (V.unsafeIndex v n))
#if MIN_VERSION_base(4,9,0)
data Proxy (s :: Meta) (f :: Type -> Type) a = Proxy
#else
data Proxy s (f :: * -> *) a = Proxy
#endif
instance (FromField a, Selector s) => GFromRecordProd (M1 S s (K1 i a)) NamedRecord where
gparseRecordProd n = (n + 1, \v -> (M1 . K1) <$> v .: name)
where
name = T.encodeUtf8 (T.pack (selName (Proxy :: Proxy s f a)))
class GToRecord a f where
gtoRecord :: a p -> [f]
instance GToRecord U1 f where
gtoRecord U1 = []
instance (GToRecord a f, GToRecord b f) => GToRecord (a :*: b) f where
gtoRecord (a :*: b) = gtoRecord a ++ gtoRecord b
instance (GToRecord a f, GToRecord b f) => GToRecord (a :+: b) f where
gtoRecord (L1 a) = gtoRecord a
gtoRecord (R1 b) = gtoRecord b
instance GToRecord a f => GToRecord (M1 D c a) f where
gtoRecord (M1 a) = gtoRecord a
instance GToRecord a f => GToRecord (M1 C c a) f where
gtoRecord (M1 a) = gtoRecord a
instance GToRecord a Field => GToRecord (M1 S c a) Field where
gtoRecord (M1 a) = gtoRecord a
instance ToField a => GToRecord (K1 i a) Field where
gtoRecord (K1 a) = [toField a]
instance (ToField a, Selector s) => GToRecord (M1 S s (K1 i a)) (B.ByteString, B.ByteString) where
gtoRecord m@(M1 (K1 a)) = [T.encodeUtf8 (T.pack (selName m)) .= toField a]
#endif