Skip to content

Commit 7000997

Browse files
committed
Add Optional helper for record sugar
1 parent 023f599 commit 7000997

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

src/Data/Codec/Argonaut/Record.purs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Data.Codec.Argonaut.Record where
22

33
import Data.Codec.Argonaut as CA
4+
import Data.Maybe (Maybe)
45
import Data.Symbol (class IsSymbol)
56
import Prim.Row as R
67
import Prim.RowList as RL
@@ -9,6 +10,11 @@ import Type.Equality as TE
910
import Type.Proxy (Proxy(..))
1011
import Unsafe.Coerce (unsafeCoerce)
1112

13+
data Optional a = Optional (CA.JsonCodec a)
14+
15+
unOptional a. Optional a CA.JsonCodec a
16+
unOptional (Optional codec) = codec
17+
1218
-- | Constructs a `JsonCodec` for a `Record` from a name and a record of codecs.
1319
-- | The name is used in the error message produced when decoding fails.
1420
-- |
@@ -46,14 +52,29 @@ class RowListCodec (rl ∷ RL.RowList Type) (ri ∷ Row Type) (ro ∷ Row Type)
4652
instance rowListCodecNilRowListCodec RL.Nil () () where
4753
rowListCodec _ _ = CA.record
4854

49-
instance rowListCodecCons
55+
instance rowListCodecConsOptional
56+
( RowListCodec rs ri' ro'
57+
, R.Cons sym (Optional a) ri' ri
58+
, R.Cons sym (Maybe a) ro' ro
59+
, IsSymbol sym
60+
)
61+
RowListCodec (RL.Cons sym (Optional a) rs) ri ro where
62+
rowListCodec _ codecs =
63+
CA.recordPropOptional (Proxy Proxy sym) codec tail
64+
where
65+
codec CA.JsonCodec a
66+
codec = TE.from (unOptional (Rec.get (Proxy Proxy sym) codecs))
67+
68+
tail CA.JPropCodec (Record ro')
69+
tail = rowListCodec (Proxy Proxy rs) ((unsafeCoerce Record ri Record ri') codecs)
70+
71+
else instance rowListCodecCons
5072
( RowListCodec rs ri' ro'
5173
, R.Cons sym (CA.JsonCodec a) ri' ri
5274
, R.Cons sym a ro' ro
5375
, IsSymbol sym
54-
, TE.TypeEquals co (CA.JsonCodec a)
5576
)
56-
RowListCodec (RL.Cons sym co rs) ri ro where
77+
RowListCodec (RL.Cons sym (CA.JsonCodec a) rs) ri ro where
5778
rowListCodec _ codecs =
5879
CA.recordProp (Proxy Proxy sym) codec tail
5980
where

test/Test/Record.purs

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ import Prelude
55
import Control.Monad.Gen as Gen
66
import Control.Monad.Gen.Common as GenC
77
import Data.Argonaut.Core (stringify)
8-
import Data.Codec.Argonaut.Common as JA
9-
import Data.Codec.Argonaut.Record as JAR
10-
import Data.Maybe (Maybe(..))
8+
import Data.Argonaut.Core as Json
9+
import Data.Codec.Argonaut.Common as CA
10+
import Data.Codec.Argonaut.Record as CAR
11+
import Data.Maybe (Maybe(..), fromJust)
1112
import Data.Newtype (class Newtype, unwrap, wrap)
1213
import Data.Profunctor (dimap)
1314
import Data.String.Gen (genAsciiString)
1415
import Effect (Effect)
1516
import Effect.Console (log)
16-
import Test.QuickCheck (quickCheck)
17+
import Foreign.Object as Object
18+
import Partial.Unsafe (unsafePartial)
19+
import Test.QuickCheck (assertEquals, quickCheck, quickCheckGen)
1720
import Test.QuickCheck.Gen (Gen)
1821
import Test.Util (genInt, propCodec)
1922

@@ -26,14 +29,15 @@ type OuterR =
2629
type InnerR =
2730
{ n Int
2831
, m Boolean
32+
, o Maybe Boolean
2933
}
3034

3135
newtype Outer = Outer OuterR
3236

3337
derive instance newtypeOuterNewtype Outer _
3438

3539
instance showOuterShow Outer where
36-
show (Outer r) = "Outer " <> stringify (JA.encode outerCodec r)
40+
show (Outer r) = "Outer " <> stringify (CA.encode outerCodec r)
3741

3842
instance eqOuterEq Outer where
3943
eq (Outer o1) (Outer o2) =
@@ -44,19 +48,20 @@ instance eqOuter ∷ Eq Outer where
4448
Just i1, Just i2 → i1.n == i2.n && i1.m == i2.m
4549
_, _ → false
4650

47-
outerCodec JA.JsonCodec OuterR
51+
outerCodec CA.JsonCodec OuterR
4852
outerCodec =
49-
JA.object "Outer" $ JAR.record
50-
{ a: JA.int
51-
, b: JA.string
52-
, c: JA.maybe innerCodec
53+
CA.object "Outer" $ CAR.record
54+
{ a: CA.int
55+
, b: CA.string
56+
, c: CA.maybe innerCodec
5357
}
5458

55-
innerCodec JA.JsonCodec InnerR
59+
innerCodec CA.JsonCodec InnerR
5660
innerCodec =
57-
JA.object "Inner" $ JAR.record
58-
{ n: JA.int
59-
, m: JA.boolean
61+
CA.object "Inner" $ CAR.record
62+
{ n: CA.int
63+
, m: CA.boolean
64+
, o: CAR.Optional CA.boolean
6065
}
6166

6267
genOuter Gen OuterR
@@ -70,9 +75,25 @@ genInner ∷ Gen InnerR
7075
genInner = do
7176
n ← genInt
7277
m ← Gen.chooseBool
73-
pure { n, m }
78+
o ← GenC.genMaybe Gen.chooseBool
79+
pure { n, m, o }
7480

7581
main Effect Unit
7682
main = do
7783
log "Checking record codec"
7884
quickCheck $ propCodec (Outer <$> genOuter) (dimap unwrap wrap outerCodec)
85+
86+
log "Check optional Nothing is missing from json"
87+
quickCheckGen do
88+
v ← genInner
89+
let obj = unsafePartial $ fromJust $ Json.toObject $ CA.encode innerCodec (v { o = Nothing })
90+
pure $ assertEquals [ "m", "n" ] (Object.keys obj)
91+
92+
log "Check optional Just is present in the json"
93+
quickCheckGen do
94+
b ← Gen.chooseBool
95+
v ← genInner
96+
let obj = unsafePartial $ fromJust $ Json.toObject $ CA.encode innerCodec (v { o = Just b })
97+
pure $ assertEquals [ "m", "n", "o" ] (Object.keys obj)
98+
99+
pure unit

0 commit comments

Comments
 (0)