Skip to content

Commit 0463c50

Browse files
Improve generics and add tests (#3)
* Improve generics and add tests * Streamline generic function names To be more inline with genericShow, genericRead, etc. to all start with genericReadForeign... or genericWriteForeign... for best dis- coverability when writing code * Add more variants of Variants * Spago install newtype * Add info in README
1 parent e306125 commit 0463c50

27 files changed

+846
-791
lines changed

.tidyrc.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
"ribbon": 1,
77
"typeArrowPlacement": "last",
88
"unicode": "always",
9-
"width": 80
9+
"width": 160
1010
}

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"purescript.buildCommand": "spago -x test.dhall build -- --purs-args --json-errors"
2+
"purescript.buildCommand": "spago -x test.dhall build --purs-args --json-errors"
33
}

README.md

+25-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88

99
## Usage
1010

11+
```purescript
12+
import Yoga.JSON as JSON
13+
14+
serialised :: String
15+
serialised =
16+
JSON.writeJSON { first_name: "Lola", last_name: "Flores" }
17+
```
18+
19+
Check out the tests for how to encode/decode increasingly complex types.
1120

1221
## Migrate from `purescript-simple-json`
1322

@@ -17,5 +26,19 @@
1726

1827
There is an inbuilt codec for `Tuple`s thanks to @ursi
1928

20-
It includes @justinwoo's codecs for en- and decoding generics
21-
from [simple-json-generics](https://github.com/justinwoo/purescript-simple-json-generics)
29+
It includes @justinwoo's codecs for en- and decoding generics inspired by
30+
[simple-json-generics](https://github.com/justinwoo/purescript-simple-json-generics)
31+
32+
### 💣 The Variant Codec is different
33+
If you want to emulate `simple-json`'s format you may use the newtype `TaggedVariant`
34+
35+
```purescript
36+
type YourVariantRow = ( a :: Int, b :: String )
37+
type YourVariant = Variant YourVariantRow
38+
x :: YourVariant
39+
x = inj (Proxy :: Proxy "a") 5
40+
-- encoded = writeJSON x
41+
-- ^ Let's say you had this before
42+
-- You can now do:
43+
encoded = writeJSON (TaggedVariant "type" "value" x)
44+
```

packages.dhall

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
let upstream =
2-
https://github.com/purescript/package-sets/releases/download/psc-0.15.0-20220509/packages.dhall
3-
sha256:d4c1a03606efdbb7bb7745a9d3aa908cb1ba5611921373659a4c7bf1c41c106c
2+
https://github.com/purescript/package-sets/releases/download/psc-0.15.2-20220610/packages.dhall
3+
sha256:348212b7c79da7d343bed71b48ed164d426f1977f92196babac49bd560b32e75
44

55
in upstream

spago.dhall

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
, "identity"
1515
, "lists"
1616
, "maybe"
17+
, "newtype"
1718
, "nullable"
1819
, "partial"
1920
, "prelude"

src/Yoga/Generics.purs

-8
This file was deleted.

src/Yoga/Generics/TaggedSumRep.purs

-53
This file was deleted.

src/Yoga/Generics/UntaggedProductRep.purs

-49
This file was deleted.

src/Yoga/Generics/UntaggedSumRep.purs

-42
This file was deleted.

src/Yoga/JSON.purs

+19-44
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ import Prelude
3535

3636
import Control.Alt ((<|>))
3737
import Control.Apply (lift2)
38-
import Control.Monad.Except (ExceptT(..), except, runExcept, runExceptT, throwError, withExcept)
38+
import Control.Monad.Except (ExceptT(..), except, runExcept, throwError, withExcept)
3939
import Data.Array as Array
4040
import Data.Array.NonEmpty (NonEmptyArray, fromArray, toArray)
4141
import Data.Bifunctor (lmap)
42-
import Data.Either (Either(..), hush, note)
42+
import Data.Either (Either, hush, note)
4343
import Data.Identity (Identity(..))
4444
import Data.List.NonEmpty (singleton)
4545
import Data.Maybe (Maybe(..), fromMaybe, maybe)
@@ -272,37 +272,20 @@ instance
272272
, Row.Cons name ty from' to
273273
) ⇒
274274
ReadForeignFields (Cons name ty tail) from to where
275-
getFields _ obj = (compose <$> first) `exceptTApply` rest
275+
getFields _ obj = do
276+
compose <$> first <*> rest
276277
where
277-
first = do
278-
value ← withExcept' (readImpl =<< readProp name obj)
279-
pure $ Builder.insert nameP value
278+
value = withExcept' (readImpl =<< readProp name obj)
279+
first = Builder.insert nameP <$> value
280280
rest = getFields tailP obj
281281
nameP = ProxyProxy name
282282
tailP = ProxyProxy tail
283283
name = reflectSymbol nameP
284-
withExcept' = withExcept <<< map $ ErrorAtProperty name
284+
withExcept' = (withExcept <<< map) (ErrorAtProperty name)
285285

286286
readAtIdx a. ReadForeign a Int Foreign F a
287287
readAtIdx i f = withExcept (map (ErrorAtIndex i)) (readImpl f)
288288

289-
exceptTApply
290-
a b e m.
291-
Semigroup e
292-
Applicative m
293-
ExceptT e m (a b)
294-
ExceptT e m a
295-
ExceptT e m b
296-
exceptTApply fun a = ExceptT $ applyEither
297-
<$> runExceptT fun
298-
<*> runExceptT a
299-
300-
applyEither e a b. Semigroup e Either e (a b) Either e a Either e b
301-
applyEither (Left e) (Right _) = Left e
302-
applyEither (Left e1) (Left e2) = Left (e1 <> e2)
303-
applyEither (Right _) (Left e) = Left e
304-
applyEither (Right fun) (Right a) = Right (fun a)
305-
306289
instance
307290
ReadForeignFields Nil () () where
308291
getFields _ _ =
@@ -323,31 +306,25 @@ class
323306
Foreign
324307
F (Variant row)
325308

326-
instance
327-
ReadForeignVariant Nil trash where
328-
readVariantImpl _ _ = fail $ ForeignError
329-
"Unable to match any variant member."
330-
331309
instance
332310
( IsSymbol name
333311
, ReadForeign ty
334312
, Row.Cons name ty trash row
335313
, ReadForeignVariant tail row
336314
) ⇒
337315
ReadForeignVariant (Cons name ty tail) row where
338-
readVariantImpl _ o =
339-
do
340-
obj { type String, value Foreign } ← readImpl o
341-
if obj.type == name then do
342-
value tyreadImpl obj.value
343-
pure $ inj namep value
344-
else
345-
(fail <<< ForeignError $ "Did not match variant tag " <> name)
346-
<|> readVariantImpl (Proxy Proxy tail) o
316+
readVariantImpl _ o = readVariantImpl (Proxy :: Proxy tail) o <|> ado
317+
value :: ty <- readProp name o >>= readImpl
318+
in inj namep value
347319
where
348320
namep = ProxyProxy name
349321
name = reflectSymbol namep
350322

323+
instance
324+
ReadForeignVariant Nil trash where
325+
readVariantImpl _ _ = fail $ ForeignError
326+
"Unable to match any variant member."
327+
351328
-- -- | A class for writing a value into JSON
352329
-- -- | need to do this intelligently using Foreign probably, because of null and undefined whatever
353330
class WriteForeign a where
@@ -449,7 +426,7 @@ instance
449426
WriteForeignVariant Nil () where
450427
writeVariantImpl _ _ =
451428
-- a PureScript-defined variant cannot reach this path, but a JavaScript FFI one could.
452-
unsafeCrashWith "Variant was not able to be writen row WriteForeign."
429+
unsafeCrashWith "Attempted to write empty variant."
453430

454431
instance
455432
( IsSymbol name
@@ -466,10 +443,8 @@ instance
466443
variant
467444
where
468445
namep = ProxyProxy name
469-
writeVariant value = unsafeToForeign
470-
{ type: reflectSymbol namep
471-
, value: writeImpl value
472-
}
446+
name = reflectSymbol namep
447+
writeVariant value = writeImpl $ Object.singleton name (writeImpl value)
473448

474449
instance ReadForeign a ReadForeign (NonEmptyArray a) where
475450
readImpl f = do
@@ -480,4 +455,4 @@ instance ReadForeign a ⇒ ReadForeign (NonEmptyArray a) where
480455
$ fromArray raw
481456

482457
instance writeForeignNEArrayWriteForeign a WriteForeign (NonEmptyArray a) where
483-
writeImpl a = writeImpl <<< toArray $ a
458+
writeImpl a = writeImpl <<< toArray $ a

src/Yoga/JSON/Error.purs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Yoga.JSON.Error where
2+
3+
import Prelude
4+
5+
import Foreign (ForeignError(..))
6+
7+
toJSONPath :: ForeignError -> String
8+
toJSONPath fe = "$" <> path
9+
where
10+
path = go fe
11+
go = case _ of
12+
ForeignError _ -> ""
13+
TypeMismatch _ _ -> ""
14+
ErrorAtIndex i e -> "[" <> show i <> "]" <> go e
15+
ErrorAtProperty _ (TypeMismatch _ "undefined") -> ""
16+
ErrorAtProperty prop e -> "." <> prop <> go e

src/Yoga/JSON/Generics.purs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module Yoga.JSON.Generics
2+
( module Exported
3+
) where
4+
5+
import Yoga.JSON.Generics.TaggedSumRep (class ReadGenericTaggedSumRep, class WriteGenericTaggedSumRep, Options, defaultOptions, genericReadForeignTaggedSum, genericReadForeignTaggedSumRep, genericWriteForeignTaggedSum, genericWriteForeignTaggedSumRep) as Exported
6+
import Yoga.JSON.Generics.UntaggedSumRep (class ReadGenericUntaggedSumRep, class WriteGenericUntaggedSumRep, genericReadForeignUntaggedSum, genericReadForeignUntaggedSumRep, genericWriteForeignUntaggedSum, genericWriteForeignUntaggedSumRep) as Exported
7+
import Yoga.JSON.Generics.EnumSumRep (class GenericEnumSumRep, genericEnumReadForeign, genericEnumWriteForeign, genericReadForeignEnum, genericWriteForeignEnum) as Exported
8+
import Yoga.JSON.Generics.UntaggedProductRep (class ReadGenericUntaggedProduct, class WriteGenericUntaggedProduct, Offset, genericReadForeignUntaggedProduct, genericReadForeignUntaggedProductRep, genericWriteForeignUntaggedProduct, genericWriteForeignUntaggedProductRep) as Exported

0 commit comments

Comments
 (0)