{-# LANGUAGE TemplateHaskell #-} {-| Module: IHP.Postgres.Polygon Description: Adds support for the Postgres Polygon type Copyright: (c) digitally induced GmbH, 2022 -} module IHP.Postgres.Polygon where import BasicPrelude import Database.PostgreSQL.Simple.ToField import Database.PostgreSQL.Simple.FromField import qualified Database.PostgreSQL.Simple.TypeInfo.Static as TI import Database.PostgreSQL.Simple.TypeInfo.Macro as TI import Data.ByteString.Builder (byteString, char8) import Data.Attoparsec.ByteString.Char8 hiding (Result, char8, Parser(..)) import Data.Attoparsec.Internal.Types (Parser) import IHP.Postgres.Point -- | Represents a Postgres Polygon -- -- See https://www.postgresql.org/docs/9.5/datatype-geometric.html data Polygon = Polygon { points :: [Point] } deriving (Eq, Show, Ord) instance FromField Polygon where fromField f v = if typeOid f /= $(inlineTypoid TI.polygon) then returnError Incompatible f "" else case v of Nothing -> returnError UnexpectedNull f "" Just bs -> case parseOnly parsePolygon bs of Left err -> returnError ConversionFailed f err Right val -> pure val parsePolygon :: Parser ByteString Polygon parsePolygon = do string "(" points <- parsePoint `sepBy` (char ',') string ")" pure $ Polygon points instance ToField Polygon where toField = serializePolygon serializePolygon :: Polygon -> Action serializePolygon Polygon { points } = Many $ (Plain (byteString "polygon'")): ( (intersperse (Plain $ char8 ',') $ map serializePoint' points) ++ [ Plain (char8 '\'') ]) where serializePoint' :: Point -> Action serializePoint' Point { x, y } = Many $ [ Plain (char8 '(') , toField x , Plain (char8 ',') , toField y , Plain (char8 ')') ]