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