@@ -11,6 +11,10 @@ module Data.Codec.Argonaut.Sum
1111 , sum
1212 , sumWith
1313 , taggedSum
14+ , sumFlat
15+ , class GFlatCases
16+ , gFlatCasesEncode
17+ , gFlatCasesDecode
1418 ) where
1519
1620import Prelude
@@ -23,8 +27,9 @@ import Data.Array as Array
2327import Data.Bifunctor (lmap )
2428import Data.Codec (codec' , encode )
2529import Data.Codec as Codec
26- import Data.Codec.Argonaut (JsonCodec , JsonDecodeError (..), jobject )
30+ import Data.Codec.Argonaut (JPropCodec , JsonCodec , JsonDecodeError (..), jobject )
2731import Data.Codec.Argonaut as CA
32+ import Data.Codec.Argonaut.Record as CAR
2833import Data.Either (Either (..), note )
2934import Data.Generic.Rep (class Generic , Argument (..), Constructor (..), NoArguments (..), Product (..), Sum (..), from , to )
3035import Data.Maybe (Maybe (..), maybe )
@@ -206,8 +211,8 @@ else instance gCasesConstructorManyArgs ∷
206211instance gCasesSum ∷
207212 ( GCases r1 (Constructor name lhs )
208213 , 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
211216 , Row.Union r1 r2 r
212217 , Row.Lacks name r2
213218 , IsSymbol name
@@ -216,8 +221,8 @@ instance gCasesSum ∷
216221 gCasesEncode ∷ Encoding → Record r → Sum (Constructor name lhs ) rhs → Json
217222 gCasesEncode encoding r =
218223 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
221226 r2 = unsafeDelete (Proxy @name) r ∷ Record r2
222227 in
223228 case _ of
@@ -227,8 +232,8 @@ instance gCasesSum ∷
227232 gCasesDecode ∷ Encoding → Record r → Json → Either JsonDecodeError (Sum (Constructor name lhs ) rhs )
228233 gCasesDecode encoding r tagged = do
229234 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
232237 r2 = Record .delete (Proxy @name) r ∷ Record r2
233238 let
234239 lhs = gCasesDecode encoding r1 tagged ∷ _ (Constructor name lhs )
@@ -393,7 +398,125 @@ encodeSumCase encoding tag jsons =
393398 encode jobject $ Obj .fromFoldable $ catMaybes
394399 [ tagEntry, valEntry ]
395400
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+
396518-- | Same as `Record.delete` but deleting only happens at the type level
397519-- | and the value is left untouched.
398520unsafeDelete ∷ ∀ 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