1
1
module Data.Codec.Argonaut.Sum
2
2
( Encoding (..)
3
+ , FlatEncoding
3
4
, class GCases
4
5
, class GFields
6
+ , class GFlatCases
5
7
, defaultEncoding
8
+ , defaultFlatEncoding
6
9
, enumSum
7
10
, gCasesDecode
8
11
, gCasesEncode
9
12
, gFieldsDecode
10
13
, gFieldsEncode
14
+ , gFlatCasesDecode
15
+ , gFlatCasesEncode
11
16
, sum
17
+ , sumFlat
18
+ , sumFlatWith
12
19
, sumWith
13
20
, taggedSum
14
- ) where
21
+ )
22
+ where
15
23
16
24
import Prelude
17
25
@@ -23,8 +31,9 @@ import Data.Array as Array
23
31
import Data.Bifunctor (lmap )
24
32
import Data.Codec (codec' , encode )
25
33
import Data.Codec as Codec
26
- import Data.Codec.Argonaut (JsonCodec , JsonDecodeError (..), jobject )
34
+ import Data.Codec.Argonaut (JPropCodec , JsonCodec , JsonDecodeError (..), jobject )
27
35
import Data.Codec.Argonaut as CA
36
+ import Data.Codec.Argonaut.Record as CAR
28
37
import Data.Either (Either (..), note )
29
38
import Data.Generic.Rep (class Generic , Argument (..), Constructor (..), NoArguments (..), Product (..), Sum (..), from , to )
30
39
import Data.Maybe (Maybe (..), maybe )
@@ -206,8 +215,8 @@ else instance gCasesConstructorManyArgs ∷
206
215
instance gCasesSum ∷
207
216
( GCases r1 (Constructor name lhs )
208
217
, GCases r2 rhs
209
- , Row.Cons name codecs1 () r1
210
- , Row.Cons name codecs1 r2 r
218
+ , Row.Cons name codec () r1
219
+ , Row.Cons name codec r2 r
211
220
, Row.Union r1 r2 r
212
221
, Row.Lacks name r2
213
222
, IsSymbol name
@@ -216,8 +225,8 @@ instance gCasesSum ∷
216
225
gCasesEncode ∷ Encoding → Record r → Sum (Constructor name lhs ) rhs → Json
217
226
gCasesEncode encoding r =
218
227
let
219
- codecs1 = Record .get (Proxy @name) r ∷ codecs1
220
- r1 = Record .insert (Proxy @name) codecs1 {} ∷ Record r1
228
+ codec = Record .get (Proxy @name) r ∷ codec
229
+ r1 = Record .insert (Proxy @name) codec {} ∷ Record r1
221
230
r2 = unsafeDelete (Proxy @name) r ∷ Record r2
222
231
in
223
232
case _ of
@@ -227,8 +236,8 @@ instance gCasesSum ∷
227
236
gCasesDecode ∷ Encoding → Record r → Json → Either JsonDecodeError (Sum (Constructor name lhs ) rhs )
228
237
gCasesDecode encoding r tagged = do
229
238
let
230
- codecs1 = Record .get (Proxy @name) r ∷ codecs1
231
- r1 = Record .insert (Proxy @name) codecs1 {} ∷ Record r1
239
+ codec = Record .get (Proxy @name) r ∷ codec
240
+ r1 = Record .insert (Proxy @name) codec {} ∷ Record r1
232
241
r2 = Record .delete (Proxy @name) r ∷ Record r2
233
242
let
234
243
lhs = gCasesDecode encoding r1 tagged ∷ _ (Constructor name lhs )
@@ -393,7 +402,135 @@ encodeSumCase encoding tag jsons =
393
402
encode jobject $ Obj .fromFoldable $ catMaybes
394
403
[ tagEntry, valEntry ]
395
404
405
+ type FlatEncoding (tag ∷ Symbol ) =
406
+ { tag ∷ Proxy tag
407
+ }
408
+
409
+ defaultFlatEncoding ∷ FlatEncoding " tag"
410
+ defaultFlatEncoding = { tag: Proxy }
411
+
412
+ sumFlat ∷ ∀ r rep a . GFlatCases " tag" r rep ⇒ Generic a rep ⇒ String → Record r → JsonCodec a
413
+ sumFlat = sumFlatWith defaultFlatEncoding
414
+
415
+ sumFlatWith ∷ ∀ @tag r rep a . GFlatCases tag r rep ⇒ Generic a rep ⇒ FlatEncoding tag -> String → Record r → JsonCodec a
416
+ sumFlatWith _ name r =
417
+ dimap from to $ codec' dec enc
418
+ where
419
+ dec = gFlatCasesDecode @tag r >>> (lmap $ Named name)
420
+ enc = gFlatCasesEncode @tag r
421
+
422
+ class GFlatCases ∷ Symbol → Row Type → Type → Constraint
423
+ class
424
+ GFlatCases tag r rep
425
+ where
426
+ gFlatCasesEncode ∷ Record r → rep → Json
427
+ gFlatCasesDecode ∷ Record r → Json → Either JsonDecodeError rep
428
+
429
+ instance gFlatCasesConstructorNoArg ∷
430
+ ( Row.Cons name Unit () rc
431
+ , Row.Cons tag String () rf
432
+ , IsSymbol name
433
+ , IsSymbol tag
434
+ ) ⇒
435
+ GFlatCases tag rc (Constructor name NoArguments ) where
436
+ gFlatCasesEncode ∷ Record rc → Constructor name NoArguments → Json
437
+ gFlatCasesEncode _ (Constructor NoArguments ) =
438
+ let
439
+ name = reflectSymbol (Proxy @name) ∷ String
440
+ propCodec = CAR .record {} ∷ JPropCodec { }
441
+ propCodecWithTag = CA .recordProp (Proxy @tag) CA .string propCodec ∷ JPropCodec (Record rf )
442
+ codecWithTag = CA .object (" case " <> name) propCodecWithTag ∷ JsonCodec (Record rf )
443
+ rcWithTag = Record .insert (Proxy @tag) name {} ∷ Record rf
444
+ in
445
+ CA .encode codecWithTag rcWithTag
446
+
447
+ gFlatCasesDecode ∷ Record rc → Json → Either JsonDecodeError (Constructor name NoArguments )
448
+ gFlatCasesDecode _ json = do
449
+ let
450
+ name = reflectSymbol (Proxy @name) ∷ String
451
+
452
+ propCodec = CAR .record {} ∷ JPropCodec { }
453
+ propCodecWithTag = CA .recordProp (Proxy @tag) CA .string propCodec ∷ JPropCodec (Record rf )
454
+ codecWithTag = CA .object (" case " <> name) propCodecWithTag ∷ JsonCodec (Record rf )
455
+ r ← CA .decode codecWithTag json ∷ _ (Record rf )
456
+ let actualTag = Record .get (Proxy @tag) r ∷ String
457
+
458
+ when (actualTag /= name)
459
+ $ Left
460
+ $ TypeMismatch (" Expecting tag `" <> name <> " `, got `" <> actualTag <> " `" )
461
+
462
+ pure (Constructor NoArguments )
463
+
464
+ instance gFlatCasesConstructorSingleArg ∷
465
+ ( Row.Cons name (JPropCodec (Record rf )) () rc
466
+ , Row.Lacks tag rf
467
+ , Row.Cons tag String rf rf'
468
+ , IsSymbol name
469
+ , IsSymbol tag
470
+ ) ⇒
471
+ GFlatCases tag rc (Constructor name (Argument (Record rf ))) where
472
+ gFlatCasesEncode ∷ Record rc → Constructor name (Argument (Record rf )) → Json
473
+ gFlatCasesEncode rc (Constructor (Argument rf)) =
474
+ let
475
+ name = reflectSymbol (Proxy @name) ∷ String
476
+ propCodec = Record .get (Proxy @name) rc ∷ JPropCodec (Record rf )
477
+ propCodecWithTag = CA .recordProp (Proxy @tag) CA .string propCodec ∷ JPropCodec (Record rf' )
478
+ codecWithTag = CA .object (" case " <> name) propCodecWithTag ∷ JsonCodec (Record rf' )
479
+ rcWithTag = Record .insert (Proxy @tag) name rf ∷ Record rf'
480
+ in
481
+ CA .encode codecWithTag rcWithTag
482
+
483
+ gFlatCasesDecode ∷ Record rc → Json → Either JsonDecodeError (Constructor name (Argument (Record rf )))
484
+ gFlatCasesDecode rc json = do
485
+ let
486
+ name = reflectSymbol (Proxy @name) ∷ String
487
+ propCodec = Record .get (Proxy @name) rc ∷ JPropCodec (Record rf )
488
+ propCodecWithTag = CA .recordProp (Proxy @tag) CA .string propCodec ∷ JPropCodec (Record rf' )
489
+ codecWithTag = CA .object (" case " <> name) propCodecWithTag ∷ JsonCodec (Record rf' )
490
+ r ← CA .decode codecWithTag json ∷ _ (Record rf' )
491
+
492
+ let actualTag = Record .get (Proxy @tag) r ∷ String
493
+ when (actualTag /= name)
494
+ $ Left
495
+ $ TypeMismatch (" Expecting tag `" <> name <> " `, got `" <> actualTag <> " `" )
496
+
497
+ let r' = Record .delete (Proxy @tag) r ∷ Record rf
498
+ pure (Constructor (Argument r'))
499
+
500
+ instance gFlatCasesSum ∷
501
+ ( GFlatCases tag r1 (Constructor name lhs )
502
+ , GFlatCases tag r2 rhs
503
+ , Row.Cons name codec () r1
504
+ , Row.Cons name codec r2 r
505
+ , Row.Union r1 r2 r
506
+ , Row.Lacks name r2
507
+ , IsSymbol name
508
+ ) ⇒
509
+ GFlatCases tag r (Sum (Constructor name lhs ) rhs ) where
510
+ gFlatCasesEncode ∷ Record r → Sum (Constructor name lhs ) rhs → Json
511
+ gFlatCasesEncode r =
512
+ let
513
+ codec = Record .get (Proxy @name) r ∷ codec
514
+ r1 = Record .insert (Proxy @name) codec {} ∷ Record r1
515
+ r2 = unsafeDelete (Proxy @name) r ∷ Record r2
516
+ in
517
+ case _ of
518
+ Inl lhs → gFlatCasesEncode @tag r1 lhs
519
+ Inr rhs → gFlatCasesEncode @tag r2 rhs
520
+
521
+ gFlatCasesDecode ∷ Record r → Json → Either JsonDecodeError (Sum (Constructor name lhs ) rhs )
522
+ gFlatCasesDecode r tagged = do
523
+ let
524
+ codec = Record .get (Proxy @name) r ∷ codec
525
+ r1 = Record .insert (Proxy @name) codec {} ∷ Record r1
526
+ r2 = Record .delete (Proxy @name) r ∷ Record r2
527
+ let
528
+ lhs = gFlatCasesDecode @tag r1 tagged ∷ _ (Constructor name lhs )
529
+ rhs = gFlatCasesDecode @tag r2 tagged ∷ _ rhs
530
+ (Inl <$> lhs) <|> (Inr <$> rhs)
531
+
396
532
-- | Same as `Record.delete` but deleting only happens at the type level
397
533
-- | and the value is left untouched.
398
534
unsafeDelete ∷ ∀ r1 r2 l a . IsSymbol l ⇒ Row.Lacks l r1 ⇒ Row.Cons l a r1 r2 ⇒ Proxy l → Record r2 → Record r1
399
- unsafeDelete _ r = unsafeCoerce r
535
+ unsafeDelete _ r = unsafeCoerce r
536
+
0 commit comments