99 lines
2.1 KiB
Haskell
99 lines
2.1 KiB
Haskell
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module Database.PostgreSQL.Opium.FromField
|
|
( FieldError (..)
|
|
, FromField (..)
|
|
) where
|
|
|
|
import Data.Attoparsec.Text
|
|
( Parser
|
|
, choice
|
|
, decimal
|
|
, double
|
|
, parseOnly
|
|
, signed
|
|
, string
|
|
, takeText
|
|
)
|
|
import Data.Functor (($>))
|
|
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
|
|
|
|
data FieldError
|
|
= FieldErrorUnexpectedNull
|
|
| FieldErrorInvalidOid Oid
|
|
| FieldErrorInvalidField Oid Text String
|
|
deriving (Eq, Show)
|
|
|
|
mapLeft :: (b -> c) -> Either b a -> Either c a
|
|
mapLeft f (Left l) = Left $ f l
|
|
mapLeft _ (Right r) = Right r
|
|
|
|
fromParser
|
|
:: (Oid -> Bool)
|
|
-> Parser a
|
|
-> Oid
|
|
-> Text
|
|
-> Either FieldError a
|
|
fromParser validOid parser oid field
|
|
| validOid oid =
|
|
mapLeft (FieldErrorInvalidField oid field) $ parseOnly parser field
|
|
| otherwise =
|
|
Left $ FieldErrorInvalidOid oid
|
|
|
|
class FromField a where
|
|
fromField :: Oid -> Text -> Either FieldError a
|
|
|
|
instance FromField Text where
|
|
fromField = fromParser
|
|
(Oid.text \/ Oid.character \/ Oid.characterVarying)
|
|
takeText
|
|
|
|
instance FromField String where
|
|
fromField oid text = Text.unpack <$> fromField oid text
|
|
|
|
instance FromField Int where
|
|
fromField = fromParser
|
|
(Oid.smallint \/ Oid.integer \/ Oid.bigint)
|
|
(signed decimal)
|
|
|
|
floatParser :: Parser Double
|
|
floatParser = choice
|
|
[ string "NaN" $> nan
|
|
, signed (string "Infinity" $> infinity)
|
|
, double
|
|
]
|
|
where
|
|
nan = 0 / 0
|
|
infinity = 1 / 0
|
|
|
|
instance FromField Float where
|
|
fromField = fromParser
|
|
Oid.real
|
|
(fmap double2Float floatParser)
|
|
|
|
instance FromField Double where
|
|
fromField = fromParser
|
|
(Oid.real \/ Oid.doublePrecision)
|
|
floatParser
|
|
|
|
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
|
|
fromField = fromParser
|
|
Oid.boolean
|
|
boolParser
|