11module Data.Codec.Argonaut.Sum
22 ( Encoding (..)
3+ , FlatEncoding
34 , class GCases
45 , class GFields
6+ , class GFlatCases
57 , defaultEncoding
8+ , defaultFlatEncoding
69 , enumSum
710 , gCasesDecode
811 , gCasesEncode
912 , gFieldsDecode
1013 , gFieldsEncode
14+ , gFlatCasesDecode
15+ , gFlatCasesEncode
1116 , sum
17+ , sumFlat
18+ , sumFlatWith
1219 , sumWith
1320 , taggedSum
14- ) where
21+ )
22+ where
1523
1624import Prelude
1725
@@ -23,8 +31,9 @@ import Data.Array as Array
2331import Data.Bifunctor (lmap )
2432import Data.Codec (codec' , encode )
2533import Data.Codec as Codec
26- import Data.Codec.Argonaut (JsonCodec , JsonDecodeError (..), jobject )
34+ import Data.Codec.Argonaut (JPropCodec , JsonCodec , JsonDecodeError (..), jobject )
2735import Data.Codec.Argonaut as CA
36+ import Data.Codec.Argonaut.Record as CAR
2837import Data.Either (Either (..), note )
2938import Data.Generic.Rep (class Generic , Argument (..), Constructor (..), NoArguments (..), Product (..), Sum (..), from , to )
3039import Data.Maybe (Maybe (..), maybe )
@@ -206,8 +215,8 @@ else instance gCasesConstructorManyArgs ∷
206215instance gCasesSum ∷
207216 ( GCases r1 (Constructor name lhs )
208217 , 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
211220 , Row.Union r1 r2 r
212221 , Row.Lacks name r2
213222 , IsSymbol name
@@ -216,8 +225,8 @@ instance gCasesSum ∷
216225 gCasesEncode ∷ Encoding → Record r → Sum (Constructor name lhs ) rhs → Json
217226 gCasesEncode encoding r =
218227 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
221230 r2 = unsafeDelete (Proxy @name) r ∷ Record r2
222231 in
223232 case _ of
@@ -227,8 +236,8 @@ instance gCasesSum ∷
227236 gCasesDecode ∷ Encoding → Record r → Json → Either JsonDecodeError (Sum (Constructor name lhs ) rhs )
228237 gCasesDecode encoding r tagged = do
229238 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
232241 r2 = Record .delete (Proxy @name) r ∷ Record r2
233242 let
234243 lhs = gCasesDecode encoding r1 tagged ∷ _ (Constructor name lhs )
@@ -393,7 +402,135 @@ encodeSumCase encoding tag jsons =
393402 encode jobject $ Obj .fromFoldable $ catMaybes
394403 [ tagEntry, valEntry ]
395404
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+
396532-- | Same as `Record.delete` but deleting only happens at the type level
397533-- | and the value is left untouched.
398534unsafeDelete ∷ ∀ 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