fix: JSON support

This commit is contained in:
orion 2024-03-27 12:20:33 -05:00
parent e2eb753317
commit 753d14fdd9
Signed by: orion
GPG Key ID: 6D4165AE4C928719
9 changed files with 329 additions and 64 deletions

View File

@ -1,2 +1,2 @@
bun 1.0.35 bun 1.0.11
purescript 0.15.15 purescript 0.15.15

BIN
bun.lockb

Binary file not shown.

202
package-lock.json generated Normal file
View File

@ -0,0 +1,202 @@
{
"name": "purs",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "purs",
"dependencies": {
"pg": "^8.11.3"
},
"devDependencies": {
"bun-types": "1.0.11",
"purs-tidy": "^0.10.0",
"typescript": "^5.0.0"
},
"peerDependencies": {
"postgres-range": "^1.1.4"
}
},
"node_modules/buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
"engines": {
"node": ">=4"
}
},
"node_modules/bun-types": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.0.11.tgz",
"integrity": "sha512-XaDwjnBlkdTOtBEAcXhDnPSKFMlwFK/526z0iyairYIDhZJMzZM1QU4D7XRiEI2SpKQWexn0S/LN9Mwx5xSJNg==",
"dev": true
},
"node_modules/packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
},
"node_modules/pg": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
"dependencies": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.6.2",
"pg-pool": "^3.6.1",
"pg-protocol": "^1.6.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
},
"engines": {
"node": ">= 8.0.0"
},
"optionalDependencies": {
"pg-cloudflare": "^1.1.1"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-cloudflare": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
"optional": true
},
"node_modules/pg-connection-string": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
"integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-range": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz",
"integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==",
"peer": true
},
"node_modules/purs-tidy": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/purs-tidy/-/purs-tidy-0.10.1.tgz",
"integrity": "sha512-i1QvMaDEaZXv/GWZNFWs5CISiBOkwPhG4D1S4Rw6zUCGaE+NQNWTjvwY21rifynGa2N2TiBJRC61LkORbmGxrA==",
"dev": true,
"bin": {
"purs-tidy": "bin/index.js"
}
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/typescript": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
}
}
}

View File

@ -8,12 +8,11 @@
}, },
"devDependencies": { "devDependencies": {
"bun-types": "1.0.11", "bun-types": "1.0.11",
"postgres-range": "^1.1.4",
"purs-tidy": "^0.10.0", "purs-tidy": "^0.10.0",
"spago": "next"
},
"peerDependencies": {
"typescript": "^5.0.0" "typescript": "^5.0.0"
}, },
"dependencies": { "pg-types": "^4.0.2" } "dependencies": {
"postgres-range": "^1.1.4",
"pg": "^8.11.3"
}
} }

View File

@ -12,13 +12,16 @@ workspace:
- foreign - foreign
- lists - lists
- maybe - maybe
- mmorph
- newtype - newtype
- node-buffer - node-buffer
- precise-datetime - precise-datetime
- prelude - prelude
- simple-json
- transformers - transformers
- unsafe-coerce - unsafe-coerce
test_dependencies: test_dependencies:
- foreign-object
- quickcheck - quickcheck
- spec - spec
- spec-quickcheck - spec-quickcheck
@ -45,6 +48,7 @@ workspace:
- fixed-points - fixed-points
- foldable-traversable - foldable-traversable
- foreign - foreign
- foreign-object
- fork - fork
- formatters - formatters
- free - free
@ -80,6 +84,7 @@ workspace:
- record - record
- refs - refs
- safe-coerce - safe-coerce
- simple-json
- spec - spec
- spec-quickcheck - spec-quickcheck
- st - st
@ -88,9 +93,11 @@ workspace:
- transformers - transformers
- tuples - tuples
- type-equality - type-equality
- typelevel-prelude
- unfoldable - unfoldable
- unicode - unicode
- unsafe-coerce - unsafe-coerce
- variant
extra_packages: {} extra_packages: {}
packages: packages:
aff: aff:
@ -333,6 +340,23 @@ packages:
- prelude - prelude
- strings - strings
- transformers - transformers
foreign-object:
type: registry
version: 4.1.0
integrity: sha256-q24okj6mT+yGHYQ+ei/pYPj5ih6sTbu7eDv/WU56JVo=
dependencies:
- arrays
- foldable-traversable
- functions
- gen
- lists
- maybe
- prelude
- st
- tailrec
- tuples
- typelevel-prelude
- unfoldable
fork: fork:
type: registry type: registry
version: 6.0.0 version: 6.0.0
@ -735,6 +759,20 @@ packages:
integrity: sha256-a1ibQkiUcbODbLE/WAq7Ttbbh9ex+x33VCQ7GngKudU= integrity: sha256-a1ibQkiUcbODbLE/WAq7Ttbbh9ex+x33VCQ7GngKudU=
dependencies: dependencies:
- unsafe-coerce - unsafe-coerce
simple-json:
type: registry
version: 9.0.0
integrity: sha256-K3RJaThqsszTd+TEklzZmAdDqvIHWgXIfKqlsoykU1c=
dependencies:
- arrays
- exceptions
- foreign
- foreign-object
- nullable
- prelude
- record
- typelevel-prelude
- variant
spec: spec:
type: registry type: registry
version: 7.6.0 version: 7.6.0
@ -852,6 +890,13 @@ packages:
version: 4.0.1 version: 4.0.1
integrity: sha256-Hs9D6Y71zFi/b+qu5NSbuadUQXe5iv5iWx0226vOHUw= integrity: sha256-Hs9D6Y71zFi/b+qu5NSbuadUQXe5iv5iWx0226vOHUw=
dependencies: [] dependencies: []
typelevel-prelude:
type: registry
version: 7.0.0
integrity: sha256-uFF2ph+vHcQpfPuPf2a3ukJDFmLhApmkpTMviHIWgJM=
dependencies:
- prelude
- type-equality
unfoldable: unfoldable:
type: registry type: registry
version: 6.0.0 version: 6.0.0
@ -875,3 +920,16 @@ packages:
version: 6.0.0 version: 6.0.0
integrity: sha256-IqIYW4Vkevn8sI+6aUwRGvd87tVL36BBeOr0cGAE7t0= integrity: sha256-IqIYW4Vkevn8sI+6aUwRGvd87tVL36BBeOr0cGAE7t0=
dependencies: [] dependencies: []
variant:
type: registry
version: 8.0.0
integrity: sha256-SR//zQDg2dnbB8ZHslcxieUkCeNlbMToapvmh9onTtw=
dependencies:
- enums
- lists
- maybe
- partial
- prelude
- record
- tuples
- unsafe-coerce

View File

@ -13,15 +13,18 @@ package:
- foreign - foreign
- lists - lists
- maybe - maybe
- mmorph
- newtype - newtype
- node-buffer - node-buffer
- precise-datetime - precise-datetime
- prelude - prelude
- simple-json
- transformers - transformers
- unsafe-coerce - unsafe-coerce
test: test:
main: Test.Main main: Test.Main
dependencies: dependencies:
- foreign-object
- quickcheck - quickcheck
- spec - spec
- spec-quickcheck - spec-quickcheck

View File

@ -1,4 +1,4 @@
import { getTypeParser, setTypeParser } from 'pg-types' import * as Pg from 'pg'
import * as Range from 'postgres-range' import * as Range from 'postgres-range'
export const null_ = null export const null_ = null
@ -20,18 +20,18 @@ export const modifyPgTypes = () => {
// @ts-ignore // @ts-ignore
const asString = a => a const asString = a => a
const asStringArray = getTypeParser(oid['text[]']) const asStringArray = Pg.types.getTypeParser(oid['text[]'])
const asStringRange = Range.parse const asStringRange = Range.parse
setTypeParser(oid['json'], asString) Pg.types.setTypeParser(oid['json'], asString)
setTypeParser(oid['jsonb'], asString) Pg.types.setTypeParser(oid['jsonb'], asString)
setTypeParser(oid['json[]'], asStringArray) Pg.types.setTypeParser(oid['json[]'], asStringArray)
setTypeParser(oid['jsonb[]'], asStringArray) Pg.types.setTypeParser(oid['jsonb[]'], asStringArray)
setTypeParser(oid['timestamp'], asString) Pg.types.setTypeParser(oid['timestamp'], asString)
setTypeParser(oid['timestamptz'], asString) Pg.types.setTypeParser(oid['timestamptz'], asString)
setTypeParser(oid['timestamp[]'], asStringArray) Pg.types.setTypeParser(oid['timestamp[]'], asStringArray)
setTypeParser(oid['timestamptz[]'], asStringArray) Pg.types.setTypeParser(oid['timestamptz[]'], asStringArray)
setTypeParser(oid['tsrange'], asStringRange) Pg.types.setTypeParser(oid['tsrange'], asStringRange)
setTypeParser(oid['tstzrange'], asStringRange) Pg.types.setTypeParser(oid['tstzrange'], asStringRange)
} }

View File

@ -4,13 +4,14 @@ import Prelude
import Control.Alt ((<|>)) import Control.Alt ((<|>))
import Control.Monad.Error.Class (liftEither, liftMaybe) import Control.Monad.Error.Class (liftEither, liftMaybe)
import Control.Monad.Except (Except, ExceptT, except, runExcept, runExceptT, withExceptT) import Control.Monad.Except (ExceptT, runExceptT)
import Control.Monad.Morph (hoist)
import Data.Bifunctor (lmap) import Data.Bifunctor (lmap)
import Data.DateTime (DateTime) import Data.DateTime (DateTime)
import Data.Generic.Rep (class Generic) import Data.Generic.Rep (class Generic)
import Data.List.NonEmpty (NonEmptyList) import Data.List.NonEmpty (NonEmptyList)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Newtype (unwrap, wrap) import Data.Newtype (class Newtype, unwrap, wrap)
import Data.Postgres.Raw (Raw) import Data.Postgres.Raw (Raw)
import Data.Postgres.Raw (unsafeFromForeign, unsafeToForeign) as Raw import Data.Postgres.Raw (unsafeFromForeign, unsafeToForeign) as Raw
import Data.RFC3339String as DateTime.ISO import Data.RFC3339String as DateTime.ISO
@ -18,19 +19,21 @@ import Data.Show.Generic (genericShow)
import Data.Traversable (traverse) import Data.Traversable (traverse)
import Effect (Effect) import Effect (Effect)
import Effect.Exception (error) import Effect.Exception (error)
import Foreign (ForeignError) import Foreign (ForeignError(..))
import Foreign as F import Foreign as F
import Node.Buffer (Buffer) import Node.Buffer (Buffer)
import Simple.JSON (class ReadForeign, class WriteForeign, readJSON', writeJSON)
newtype JSON = JSON String newtype JSON a = JSON a
derive instance Newtype (JSON a) _
derive newtype instance WriteForeign a => WriteForeign (JSON a)
derive newtype instance ReadForeign a => ReadForeign (JSON a)
foreign import null_ :: Raw foreign import null_ :: Raw
-- | Important! This effect MUST be evaluated to guarantee -- | This mutates `import('pg').types`, setting deserialization
-- | that (de)serialization will work for timestamp and JSON types. -- | for some types to unmarshal as strings rather than JS values.
-- |
-- | This mutates the `pg-types`, overriding the default deserialization
-- | behavior for JSON and timestamp types.
foreign import modifyPgTypes :: Effect Unit foreign import modifyPgTypes :: Effect Unit
-- | The SQL value NULL -- | The SQL value NULL
@ -43,35 +46,12 @@ instance Show Null where
show = genericShow show = genericShow
-- | The serialization & deserialization monad. -- | The serialization & deserialization monad.
type RepT a = ExceptT RepError Effect a type RepT a = ExceptT (NonEmptyList ForeignError) Effect a
-- | Errors encounterable while serializing & deserializing.
data RepError
= RepErrorTypeMismatch { expected :: String, found :: String }
| RepErrorInvalid String
| RepErrorForeign ForeignError
| RepErrorOther String
| RepErrorMultiple (NonEmptyList RepError)
derive instance Generic RepError _
derive instance Eq RepError
instance Show RepError where
show a = genericShow a
instance Semigroup RepError where
append (RepErrorMultiple as) (RepErrorMultiple bs) = RepErrorMultiple (as <> bs)
append (RepErrorMultiple as) b = RepErrorMultiple (as <> pure b)
append a (RepErrorMultiple bs) = RepErrorMultiple (pure a <> bs)
append a b = RepErrorMultiple (pure a <> pure b)
-- | Flatten to an Effect, rendering any `RepError`s to `String` using `Show`. -- | Flatten to an Effect, rendering any `RepError`s to `String` using `Show`.
smash :: forall a. RepT a -> Effect a smash :: forall a. RepT a -> Effect a
smash = liftEither <=< map (lmap (error <<< show)) <<< runExceptT smash = liftEither <=< map (lmap (error <<< show)) <<< runExceptT
-- | Lift an `Except` returned by functions in the `Foreign` module to `RepT`
liftForeign :: forall a. Except (NonEmptyList ForeignError) a -> RepT a
liftForeign = except <<< runExcept <<< withExceptT (RepErrorMultiple <<< map RepErrorForeign)
-- | Serialize data of type `a` to a `Raw` SQL value. -- | Serialize data of type `a` to a `Raw` SQL value.
class Serialize a where class Serialize a where
serialize :: a -> RepT Raw serialize :: a -> RepT Raw
@ -95,6 +75,9 @@ instance Serialize Raw where
instance Serialize Null where instance Serialize Null where
serialize _ = unsafeSerializeCoerce null_ serialize _ = unsafeSerializeCoerce null_
instance WriteForeign a => Serialize (JSON a) where
serialize = serialize <<< writeJSON <<< unwrap
-- | `bytea` -- | `bytea`
instance Serialize Buffer where instance Serialize Buffer where
serialize = unsafeSerializeCoerce serialize = unsafeSerializeCoerce
@ -127,36 +110,39 @@ instance Serialize a => Serialize (Array a) where
instance Deserialize Raw where instance Deserialize Raw where
deserialize = pure deserialize = pure
instance Deserialize Null where
deserialize = map (const Null) <<< F.readNullOrUndefined <<< Raw.unsafeToForeign
instance ReadForeign a => Deserialize (JSON a) where
deserialize = map wrap <<< (hoist (pure <<< unwrap) <<< readJSON') <=< deserialize @String
-- | `bytea` -- | `bytea`
instance Deserialize Buffer where instance Deserialize Buffer where
deserialize = liftForeign <<< (F.unsafeReadTagged "Buffer") <<< Raw.unsafeToForeign deserialize = (F.unsafeReadTagged "Buffer") <<< Raw.unsafeToForeign
instance Deserialize Null where
deserialize = map (const Null) <<< liftForeign <<< F.readNullOrUndefined <<< Raw.unsafeToForeign
instance Deserialize Int where instance Deserialize Int where
deserialize = liftForeign <<< F.readInt <<< Raw.unsafeToForeign deserialize = F.readInt <<< Raw.unsafeToForeign
instance Deserialize Boolean where instance Deserialize Boolean where
deserialize = liftForeign <<< F.readBoolean <<< Raw.unsafeToForeign deserialize = F.readBoolean <<< Raw.unsafeToForeign
instance Deserialize String where instance Deserialize String where
deserialize = liftForeign <<< F.readString <<< Raw.unsafeToForeign deserialize = F.readString <<< Raw.unsafeToForeign
instance Deserialize Number where instance Deserialize Number where
deserialize = liftForeign <<< F.readNumber <<< Raw.unsafeToForeign deserialize = F.readNumber <<< Raw.unsafeToForeign
instance Deserialize Char where instance Deserialize Char where
deserialize = liftForeign <<< F.readChar <<< Raw.unsafeToForeign deserialize = F.readChar <<< Raw.unsafeToForeign
instance Deserialize DateTime where instance Deserialize DateTime where
deserialize raw = do deserialize raw = do
s :: String <- deserialize raw s :: String <- deserialize raw
let invalid = RepErrorInvalid $ "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
instance Deserialize a => Deserialize (Array a) where instance Deserialize a => Deserialize (Array a) where
deserialize = traverse (deserialize <<< Raw.unsafeFromForeign) <=< liftForeign <<< F.readArray <<< Raw.unsafeToForeign deserialize = traverse (deserialize <<< Raw.unsafeFromForeign) <=< F.readArray <<< Raw.unsafeToForeign
instance Deserialize a => Deserialize (Maybe a) where instance Deserialize a => Deserialize (Maybe a) where
deserialize raw = deserialize raw =

View File

@ -8,8 +8,8 @@ import Data.DateTime (DateTime(..))
import Data.DateTime.Instant as Instant import Data.DateTime.Instant as Instant
import Data.Int as Int import Data.Int as Int
import Data.Maybe (Maybe, fromJust, fromMaybe, maybe) import Data.Maybe (Maybe, fromJust, fromMaybe, maybe)
import Data.Newtype (wrap) import Data.Newtype (unwrap, wrap)
import Data.Postgres (class Rep, Null(..), deserialize, null_, serialize, smash) import Data.Postgres (class Rep, JSON(..), Null(..), deserialize, null_, serialize, smash)
import Data.Postgres.Range as Range import Data.Postgres.Range as Range
import Data.Postgres.Raw (Raw) import Data.Postgres.Raw (Raw)
import Data.Postgres.Raw as Raw import Data.Postgres.Raw as Raw
@ -19,7 +19,9 @@ import Effect.Class (liftEffect)
import Effect.Console (log) import Effect.Console (log)
import Effect.Unsafe (unsafePerformEffect) import Effect.Unsafe (unsafePerformEffect)
import Foreign (unsafeToForeign) import Foreign (unsafeToForeign)
import Foreign.Object as Object
import Partial.Unsafe (unsafePartial) import Partial.Unsafe (unsafePartial)
import Simple.JSON (writeImpl, writeJSON)
import Test.QuickCheck (class Arbitrary, (==?)) import Test.QuickCheck (class Arbitrary, (==?))
import Test.Spec (Spec, describe, it) import Test.Spec (Spec, describe, it)
import Test.Spec.Assertions (shouldEqual) import Test.Spec.Assertions (shouldEqual)
@ -54,6 +56,21 @@ spec =
check @(Array String) "Array String" identity asRaw check @(Array String) "Array String" identity asRaw
check @DateTime "DateTime" dateTimeFromArbitrary (asRaw <<< DateTime.ISO.fromDateTime) check @DateTime "DateTime" dateTimeFromArbitrary (asRaw <<< DateTime.ISO.fromDateTime)
describe "JSON" do
describe "Record" do
it "deserialize" $
quickCheck \(a /\ b /\ c :: Int /\ String /\ Array {"foo" :: String}) -> unsafePerformEffect do
let
obj = {a, b, c}
json = writeJSON obj
act :: JSON _ <- smash $ deserialize $ asRaw json
pure $ obj ==? unwrap act
it "serialize" $
quickCheck \(a /\ b /\ c :: Int /\ String /\ Array {"foo" :: String}) -> unsafePerformEffect do
let obj = {a, b, c}
act <- smash $ serialize $ JSON obj
pure $ asRaw (writeJSON obj) ==? act
describe "Null" do describe "Null" do
it "serialize" $ liftEffect $ shouldEqual null_ =<< (smash $ serialize Null) it "serialize" $ liftEffect $ shouldEqual null_ =<< (smash $ serialize Null)
it "deserialize" $ liftEffect $ shouldEqual Null =<< (smash $ deserialize null_) it "deserialize" $ liftEffect $ shouldEqual Null =<< (smash $ deserialize null_)