feat: allow instances of serialize + deserialize outside this lib

This commit is contained in:
orion kindel 2024-04-05 20:06:23 -05:00
parent 07c2f5dd84
commit 5fcf8c4549
Signed by: orion
GPG Key ID: 6D4165AE4C928719
5 changed files with 66 additions and 85 deletions

View File

@ -1,5 +1,5 @@
import {readFile, writeFile} from 'fs/promises' import { readFile, writeFile } from 'fs/promises'
import {execSync} from 'child_process' import { execSync } from 'child_process'
let ver = process.argv[2] let ver = process.argv[2]
if (!ver) { if (!ver) {
@ -19,7 +19,10 @@ const spagonew = spago.replace(/version: .+/, `version: '${ver}'`)
await writeFile('./spago.yaml', spagonew) await writeFile('./spago.yaml', spagonew)
const readme = await readFile('./README.md', 'utf8') const readme = await readFile('./README.md', 'utf8')
const readmenew = readme.replace(/packages\/purescript-postgresql\/.+?\//g, `/packages/purescript-postgresql/${ver}/`) const readmenew = readme.replace(
/packages\/purescript-postgresql\/.+?\//g,
`/packages/purescript-postgresql/${ver}/`,
)
await writeFile('./README.md', readmenew) await writeFile('./README.md', readmenew)
execSync(`git add spago.yaml package.json README.md`) execSync(`git add spago.yaml package.json README.md`)

View File

@ -5,35 +5,23 @@ import Prelude
import Control.Alt ((<|>)) import Control.Alt ((<|>))
import Control.Monad.Error.Class (liftMaybe) import Control.Monad.Error.Class (liftMaybe)
import Data.Array.NonEmpty.Internal (NonEmptyArray) import Data.Array.NonEmpty.Internal (NonEmptyArray)
import Data.Either (hush)
import Data.Foldable (intercalate) import Data.Foldable (intercalate)
import Data.Generic.Rep (class Generic) import Data.Generic.Rep (class Generic)
import Data.Generic.Rep as G import Data.Generic.Rep as G
import Data.Maybe (Maybe(..), fromJust) import Data.Maybe (Maybe(..))
import Data.Newtype as Newtype import Data.Newtype as Newtype
import Data.Postgres (RepT, deserialize, serialize) import Data.Postgres (class Rep, RepT, deserialize, serialize)
import Data.Postgres.Custom (class CustomRep, quoted, typeName) import Data.Postgres.Custom (quoted)
import Data.Postgres.Query (Query, emptyQuery) import Data.Postgres.Query (Query, emptyQuery)
import Data.Postgres.Raw (Raw) import Data.Postgres.Raw (Raw)
import Data.String as String
import Data.String.Regex (Regex)
import Data.String.Regex as Regex
import Data.String.Regex.Flags as Regex.Flags
import Data.Symbol (class IsSymbol) import Data.Symbol (class IsSymbol)
import Foreign (ForeignError(..)) import Foreign (ForeignError(..))
import Partial.Unsafe (unsafePartial)
import Type.Prelude (Proxy(..), reflectSymbol) import Type.Prelude (Proxy(..), reflectSymbol)
upperRe :: Regex typeName :: forall @a ty. CustomEnum a ty => String
upperRe = unsafePartial fromJust $ hush $ Regex.regex "[A-Z]" Regex.Flags.global typeName = reflectSymbol (Proxy @ty)
leadingUnderRe :: Regex class (IsSymbol ty, Rep a) <= CustomEnum a ty | a -> ty where
leadingUnderRe = unsafePartial fromJust $ hush $ Regex.regex "^_" Regex.Flags.noFlags
pascalToSnake :: String -> String
pascalToSnake = String.toLower <<< Regex.replace leadingUnderRe "" <<< Regex.replace upperRe "_$1"
class CustomRep a ty <= CustomEnum a ty | a -> ty where
enumVariants :: NonEmptyArray a enumVariants :: NonEmptyArray a
parseEnum :: String -> Maybe a parseEnum :: String -> Maybe a
printEnum :: a -> String printEnum :: a -> String

View File

@ -3,27 +3,28 @@ module Data.Postgres.Custom where
import Prelude import Prelude
import Control.Monad.Except (ExceptT) import Control.Monad.Except (ExceptT)
import Data.Either (hush)
import Data.List.NonEmpty (NonEmptyList) import Data.List.NonEmpty (NonEmptyList)
import Data.Maybe (Maybe) import Data.Maybe (Maybe, fromJust)
import Data.Postgres.Raw (Raw) import Data.Postgres.Raw (Raw)
import Data.String as String
import Data.String.Regex (Regex)
import Data.String.Regex as Regex
import Data.String.Regex.Flags as Regex.Flags
import Effect (Effect) import Effect (Effect)
import Foreign (ForeignError) import Foreign (ForeignError)
import Partial.Unsafe (unsafePartial)
import Type.Data.Symbol (reflectSymbol) import Type.Data.Symbol (reflectSymbol)
import Type.Prelude (class IsSymbol, Proxy(..)) import Type.Prelude (class IsSymbol, Proxy(..))
class (IsSymbol ty) <= CustomSerialize a ty | a -> ty where
customPrintExpr :: a -> Maybe String
customSerialize :: a -> ExceptT (NonEmptyList ForeignError) Effect Raw
class (IsSymbol ty) <= CustomDeserialize a ty | a -> ty where
customDeserialize :: Raw -> ExceptT (NonEmptyList ForeignError) Effect a
class (IsSymbol ty, CustomSerialize a ty, CustomDeserialize a ty) <= CustomRep a ty | a -> ty
instance (IsSymbol ty, CustomSerialize a ty, CustomDeserialize a ty) => CustomRep a ty
quoted :: String -> String quoted :: String -> String
quoted s = "'" <> s <> "'" quoted s = "'" <> s <> "'"
typeName :: forall @a ty. CustomRep a ty => String upperRe :: Regex
typeName = reflectSymbol (Proxy @ty) upperRe = unsafePartial fromJust $ hush $ Regex.regex "[A-Z]" Regex.Flags.global
leadingUnderRe :: Regex
leadingUnderRe = unsafePartial fromJust $ hush $ Regex.regex "^_" Regex.Flags.noFlags
pascalToSnake :: String -> String
pascalToSnake = String.toLower <<< Regex.replace leadingUnderRe "" <<< Regex.replace upperRe "_$1"

View File

@ -12,7 +12,6 @@ import Data.DateTime (DateTime)
import Data.List.NonEmpty (NonEmptyList) import Data.List.NonEmpty (NonEmptyList)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Newtype (class Newtype, unwrap, wrap) import Data.Newtype (class Newtype, unwrap, wrap)
import Data.Postgres.Custom (class CustomDeserialize, class CustomSerialize, customDeserialize, customSerialize)
import Data.Postgres.Range (Range, __rangeFromRecord, __rangeRawFromRaw, __rangeRawFromRecord, __rangeRawToRecord, __rangeToRecord) import Data.Postgres.Range (Range, __rangeFromRecord, __rangeRawFromRaw, __rangeRawFromRecord, __rangeRawToRecord, __rangeToRecord)
import Data.Postgres.Raw (Null(..), Raw, jsNull) import Data.Postgres.Raw (Null(..), Raw, jsNull)
import Data.Postgres.Raw (unsafeFromForeign, asForeign) as Raw import Data.Postgres.Raw (unsafeFromForeign, asForeign) as Raw
@ -73,87 +72,84 @@ instance Serialize Raw where
serialize = pure serialize = pure
-- | `NULL` -- | `NULL`
else instance Serialize Unit where instance Serialize Unit where
serialize _ = serialize Null serialize _ = serialize Null
-- | `NULL` -- | `NULL`
else instance Serialize Null where instance Serialize Null where
serialize _ = unsafeSerializeCoerce jsNull serialize _ = unsafeSerializeCoerce jsNull
-- | `json`, `jsonb` -- | `json`, `jsonb`
else instance WriteForeign a => Serialize (JSON a) where instance WriteForeign a => Serialize (JSON a) where
serialize = serialize <<< writeJSON <<< unwrap serialize = serialize <<< writeJSON <<< unwrap
-- | `bytea` -- | `bytea`
else instance Serialize Buffer where instance Serialize Buffer where
serialize = unsafeSerializeCoerce serialize = unsafeSerializeCoerce
-- | `int2`, `int4` -- | `int2`, `int4`
else instance Serialize Int where instance Serialize Int where
serialize = unsafeSerializeCoerce serialize = unsafeSerializeCoerce
-- | `int8` -- | `int8`
else instance Serialize BigInt where instance Serialize BigInt where
serialize = serialize <<< BigInt.toString serialize = serialize <<< BigInt.toString
-- | `bool` -- | `bool`
else instance Serialize Boolean where instance Serialize Boolean where
serialize = unsafeSerializeCoerce serialize = unsafeSerializeCoerce
-- | `text`, `inet`, `tsquery`, `tsvector`, `uuid`, `xml`, `cidr`, `time`, `timetz` -- | `text`, `inet`, `tsquery`, `tsvector`, `uuid`, `xml`, `cidr`, `time`, `timetz`
else instance Serialize String where instance Serialize String where
serialize = unsafeSerializeCoerce serialize = unsafeSerializeCoerce
-- | `float4`, `float8` -- | `float4`, `float8`
else instance Serialize Number where instance Serialize Number where
serialize = unsafeSerializeCoerce serialize = unsafeSerializeCoerce
-- | `timestamp`, `timestamptz` -- | `timestamp`, `timestamptz`
else instance Serialize DateTime where instance Serialize DateTime where
serialize = serialize <<< unwrap <<< DateTime.ISO.fromDateTime serialize = serialize <<< unwrap <<< DateTime.ISO.fromDateTime
-- | `Just` -> `a`, `Nothing` -> `NULL` -- | `Just` -> `a`, `Nothing` -> `NULL`
else instance Serialize a => Serialize (Maybe a) where instance Serialize a => Serialize (Maybe a) where
serialize (Just a) = serialize a serialize (Just a) = serialize a
serialize Nothing = unsafeSerializeCoerce jsNull serialize Nothing = unsafeSerializeCoerce jsNull
-- | postgres `array` -- | postgres `array`
else instance Serialize a => Serialize (Array a) where instance Serialize a => Serialize (Array a) where
serialize = unsafeSerializeCoerce <=< traverse serialize serialize = unsafeSerializeCoerce <=< traverse serialize
else instance (Ord a, Rep a) => Serialize (Range a) where instance (Ord a, Rep a) => Serialize (Range a) where
serialize = serialize =
map (Raw.unsafeFromForeign <<< unsafeToForeign <<< __rangeRawFromRecord <<< __rangeToRecord) map (Raw.unsafeFromForeign <<< unsafeToForeign <<< __rangeRawFromRecord <<< __rangeToRecord)
<<< traverse serialize <<< traverse serialize
else instance (CustomSerialize a ty) => Serialize a where
serialize = customSerialize
instance Deserialize Raw where instance Deserialize Raw where
deserialize = pure deserialize = pure
-- | `NULL` (always succeeds) -- | `NULL` (always succeeds)
else instance Deserialize Unit where instance Deserialize Unit where
deserialize _ = pure unit deserialize _ = pure unit
-- | `NULL` (fails if non-null) -- | `NULL` (fails if non-null)
else instance Deserialize Null where instance Deserialize Null where
deserialize = map (const Null) <<< F.readNullOrUndefined <<< Raw.asForeign deserialize = map (const Null) <<< F.readNullOrUndefined <<< Raw.asForeign
-- | `json`, `jsonb` -- | `json`, `jsonb`
else instance ReadForeign a => Deserialize (JSON a) where instance ReadForeign a => Deserialize (JSON a) where
deserialize = map wrap <<< (hoist (pure <<< unwrap) <<< readJSON') <=< deserialize @String deserialize = map wrap <<< (hoist (pure <<< unwrap) <<< readJSON') <=< deserialize @String
-- | `bytea` -- | `bytea`
else instance Deserialize Buffer where instance Deserialize Buffer where
deserialize = (F.unsafeReadTagged "Buffer") <<< Raw.asForeign deserialize = (F.unsafeReadTagged "Buffer") <<< Raw.asForeign
-- | `int2`, `int4` -- | `int2`, `int4`
else instance Deserialize Int where instance Deserialize Int where
deserialize = F.readInt <<< Raw.asForeign deserialize = F.readInt <<< Raw.asForeign
-- | `int8` -- | `int8`
else instance Deserialize BigInt where instance Deserialize BigInt where
deserialize = deserialize =
let let
invalid s = pure $ ForeignError $ "Invalid bigint: " <> s invalid s = pure $ ForeignError $ "Invalid bigint: " <> s
@ -162,30 +158,30 @@ else instance Deserialize BigInt where
fromString <=< deserialize @String fromString <=< deserialize @String
-- | `bool` -- | `bool`
else instance Deserialize Boolean where instance Deserialize Boolean where
deserialize = F.readBoolean <<< Raw.asForeign deserialize = F.readBoolean <<< Raw.asForeign
-- | `text`, `inet`, `tsquery`, `tsvector`, `uuid`, `xml`, `cidr`, `time`, `timetz` -- | `text`, `inet`, `tsquery`, `tsvector`, `uuid`, `xml`, `cidr`, `time`, `timetz`
else instance Deserialize String where instance Deserialize String where
deserialize = F.readString <<< Raw.asForeign deserialize = F.readString <<< Raw.asForeign
-- | `float4`, `float8` -- | `float4`, `float8`
else instance Deserialize Number where instance Deserialize Number where
deserialize = F.readNumber <<< Raw.asForeign deserialize = F.readNumber <<< Raw.asForeign
-- | `timestamp`, `timestamptz` -- | `timestamp`, `timestamptz`
else instance Deserialize DateTime where instance Deserialize DateTime where
deserialize raw = do deserialize raw = do
s :: String <- deserialize raw s :: String <- deserialize raw
let invalid = pure $ ForeignError $ "Not a valid ISO8601 string: `" <> s <> "`" let invalid = pure $ ForeignError $ "Not a valid ISO8601 string: `" <> s <> "`"
liftMaybe invalid $ DateTime.ISO.toDateTime $ wrap s liftMaybe invalid $ DateTime.ISO.toDateTime $ wrap s
-- | postgres `array` -- | postgres `array`
else instance Deserialize a => Deserialize (Array a) where instance Deserialize a => Deserialize (Array a) where
deserialize = traverse (deserialize <<< Raw.unsafeFromForeign) <=< F.readArray <<< Raw.asForeign deserialize = traverse (deserialize <<< Raw.unsafeFromForeign) <=< F.readArray <<< Raw.asForeign
-- | non-NULL -> `Just`, NULL -> `Nothing` -- | non-NULL -> `Just`, NULL -> `Nothing`
else instance Deserialize a => Deserialize (Maybe a) where instance Deserialize a => Deserialize (Maybe a) where
deserialize raw = deserialize raw =
let let
nothing = const Nothing <$> deserialize @Null raw nothing = const Nothing <$> deserialize @Null raw
@ -193,8 +189,5 @@ else instance Deserialize a => Deserialize (Maybe a) where
in in
just <|> nothing just <|> nothing
else instance (Ord a, Rep a) => Deserialize (Range a) where instance (Ord a, Rep a) => Deserialize (Range a) where
deserialize = traverse deserialize <=< map (__rangeFromRecord <<< __rangeRawToRecord) <<< lift <<< __rangeRawFromRaw deserialize = traverse deserialize <=< map (__rangeFromRecord <<< __rangeRawToRecord) <<< lift <<< __rangeRawFromRaw
else instance (CustomDeserialize a ty) => Deserialize a where
deserialize = customDeserialize

View File

@ -9,8 +9,7 @@ import Data.DateTime (DateTime(..))
import Data.Generic.Rep (class Generic) import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Newtype (unwrap) import Data.Newtype (unwrap)
import Data.Postgres (deserialize, serialize, smash) import Data.Postgres (class Deserialize, class Serialize, deserialize, serialize, smash)
import Data.Postgres.Custom (class CustomDeserialize, class CustomSerialize, customDeserialize)
import Data.Postgres.Custom.Enum (class CustomEnum, create, enumDeserialize, enumPrintExpr, enumSerialize, genericEnumVariants, genericParseEnum, genericPrintEnum, parseEnum, printEnum) import Data.Postgres.Custom.Enum (class CustomEnum, create, enumDeserialize, enumPrintExpr, enumSerialize, genericEnumVariants, genericParseEnum, genericPrintEnum, parseEnum, printEnum)
import Data.Show.Generic (genericShow) import Data.Show.Generic (genericShow)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
@ -25,12 +24,11 @@ derive instance Eq Enum1
instance Show Enum1 where instance Show Enum1 where
show = genericShow show = genericShow
instance CustomSerialize Enum1 "enum_1" where instance Serialize Enum1 where
customPrintExpr a = enumPrintExpr a serialize a = enumSerialize a
customSerialize a = enumSerialize a
instance CustomDeserialize Enum1 "enum_1" where instance Deserialize Enum1 where
customDeserialize a = enumDeserialize a deserialize a = enumDeserialize a
instance CustomEnum Enum1 "enum_1" where instance CustomEnum Enum1 "enum_1" where
printEnum = genericPrintEnum printEnum = genericPrintEnum
@ -44,12 +42,11 @@ derive instance Eq Enum2
instance Show Enum2 where instance Show Enum2 where
show = genericShow show = genericShow
instance CustomSerialize Enum2 "enum_2" where instance Serialize Enum2 where
customPrintExpr a = enumPrintExpr a serialize a = enumSerialize a
customSerialize a = enumSerialize a
instance CustomDeserialize Enum2 "enum_2" where instance Deserialize Enum2 where
customDeserialize a = enumDeserialize a deserialize a = enumDeserialize a
instance CustomEnum Enum2 "enum_2" where instance CustomEnum Enum2 "enum_2" where
printEnum a = genericPrintEnum a printEnum a = genericPrintEnum a
@ -63,12 +60,11 @@ derive instance Eq Enum5
instance Show Enum5 where instance Show Enum5 where
show = genericShow show = genericShow
instance CustomSerialize Enum5 "enum_5" where instance Serialize Enum5 where
customPrintExpr a = enumPrintExpr a serialize a = enumSerialize a
customSerialize a = enumSerialize a
instance CustomDeserialize Enum5 "enum_5" where instance Deserialize Enum5 where
customDeserialize a = enumDeserialize a deserialize a = enumDeserialize a
instance CustomEnum Enum5 "enum_5" where instance CustomEnum Enum5 "enum_5" where
printEnum a = genericPrintEnum a printEnum a = genericPrintEnum a