101 lines
2.7 KiB
Haskell
101 lines
2.7 KiB
Haskell
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE TypeApplications #-}
|
|
|
|
module Database.PostgreSQL.Opium.FromField
|
|
( FromField (..)
|
|
, fromField
|
|
) where
|
|
|
|
import Data.Attoparsec.Text
|
|
( Parser
|
|
, anyChar
|
|
, choice
|
|
, decimal
|
|
, double
|
|
, parseOnly
|
|
, signed
|
|
, string
|
|
, takeText
|
|
)
|
|
import Data.Functor (($>))
|
|
import Data.Proxy (Proxy (..))
|
|
import Data.Text (Text)
|
|
import Database.PostgreSQL.LibPQ (Oid)
|
|
import GHC.Float (double2Float)
|
|
|
|
import qualified Data.Text as Text
|
|
|
|
import qualified Database.PostgreSQL.Opium.Oid as Oid
|
|
|
|
(\/) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
|
|
p \/ q = \x -> p x || q x
|
|
|
|
fromField :: FromField a => Text -> Either String a
|
|
fromField =
|
|
parseOnly parseField
|
|
|
|
class FromField a where
|
|
validOid :: Proxy a -> Oid -> Bool
|
|
parseField :: Parser a
|
|
|
|
-- | See https://www.postgresql.org/docs/current/datatype-character.html.
|
|
instance FromField Text where
|
|
validOid Proxy = Oid.text \/ Oid.character \/ Oid.characterVarying
|
|
parseField = takeText
|
|
|
|
-- | See https://www.postgresql.org/docs/current/datatype-character.html.
|
|
instance FromField String where
|
|
validOid Proxy = validOid @Text Proxy
|
|
parseField = Text.unpack <$> parseField
|
|
|
|
-- | See https://www.postgresql.org/docs/current/datatype-character.html.
|
|
-- This instance accepts all character types but fails to decode fields that are not exactly one character.
|
|
instance FromField Char where
|
|
validOid Proxy = validOid @Text Proxy
|
|
parseField = anyChar
|
|
|
|
-- | See https://www.postgresql.org/docs/current/datatype-numeric.html.
|
|
-- We assume that 'Int' has 64 bits. This is not guaranteed but reasonable enough.
|
|
instance FromField Int where
|
|
validOid Proxy = Oid.smallint \/ Oid.integer \/ Oid.bigint
|
|
parseField = signed decimal
|
|
|
|
-- | See https://www.postgresql.org/docs/current/datatype-numeric.html.
|
|
instance FromField Integer where
|
|
validOid Proxy = Oid.smallint \/ Oid.integer \/ Oid.bigint
|
|
parseField = signed decimal
|
|
|
|
instance FromField Word where
|
|
validOid Proxy = Oid.smallint \/ Oid.integer \/ Oid.bigint
|
|
parseField = decimal
|
|
|
|
doubleParser :: Parser Double
|
|
doubleParser = choice
|
|
[ string "NaN" $> nan
|
|
, signed (string "Infinity" $> infinity)
|
|
, double
|
|
]
|
|
where
|
|
nan = 0 / 0
|
|
infinity = 1 / 0
|
|
|
|
instance FromField Float where
|
|
validOid Proxy = Oid.real
|
|
parseField = fmap double2Float doubleParser
|
|
|
|
instance FromField Double where
|
|
validOid Proxy = Oid.real \/ Oid.doublePrecision
|
|
parseField = doubleParser
|
|
|
|
boolParser :: Parser Bool
|
|
boolParser = choice
|
|
[ string "t" $> True
|
|
, string "f" $> False
|
|
]
|
|
|
|
-- | See https://www.postgresql.org/docs/current/datatype-boolean.html.
|
|
instance FromField Bool where
|
|
validOid Proxy = Oid.boolean
|
|
parseField = boolParser
|