Skip to content

Commit f5378fd

Browse files
author
Serhii Khoma
authored
Upgrade argonaut-codecs, which uses typed errors (#22)
1 parent f17e2d1 commit f5378fd

File tree

3 files changed

+45
-38
lines changed

3 files changed

+45
-38
lines changed

bower.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
"license": "MIT",
1818
"dependencies": {
1919
"purescript-argonaut-core": "^5.0.0",
20-
"purescript-argonaut-codecs": "^6.0.2",
20+
"purescript-argonaut-codecs": "^7.0.0",
2121
"purescript-generics-rep": "^6.1.1",
2222
"purescript-record": "^2.0.0"
2323
},
2424
"devDependencies": {
2525
"purescript-assert": "^4.1.0",
26-
"purescript-console": "^4.2.0"
26+
"purescript-console": "^4.2.0",
27+
"purescript-exceptions": "^4.0.0"
2728
}
2829
}

src/Data/Argonaut/Decode/Generic/Rep.purs

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ module Data.Argonaut.Decode.Generic.Rep (
1515
import Prelude
1616

1717
import Control.Alt ((<|>))
18-
import Data.Argonaut.Core (Json, toArray, toObject, toString)
19-
import Data.Argonaut.Decode.Class (class DecodeJson, decodeJson)
18+
import Data.Argonaut.Core (Json, fromString, toArray, toObject, toString, fromArray)
19+
import Data.Argonaut.Decode (class DecodeJson, decodeJson, JsonDecodeError(..))
2020
import Data.Argonaut.Types.Generic.Rep (Encoding, defaultEncoding)
2121
import Data.Array (uncons)
2222
import Data.Bifunctor (lmap)
@@ -28,13 +28,13 @@ import Partial.Unsafe (unsafeCrashWith)
2828
import Prim.TypeError (class Fail, Text)
2929

3030
class DecodeRep r where
31-
decodeRepWith :: Encoding -> Json -> Either String r
31+
decodeRepWith :: Encoding -> Json -> Either JsonDecodeError r
3232

33-
decodeRep :: forall r. DecodeRep r => Json -> Either String r
33+
decodeRep :: forall r. DecodeRep r => Json -> Either JsonDecodeError r
3434
decodeRep = decodeRepWith defaultEncoding
3535

3636
instance decodeRepNoConstructors :: DecodeRep Rep.NoConstructors where
37-
decodeRepWith e _ = Left "Cannot decode empty data type"
37+
decodeRepWith e _ = Left $ UnexpectedValue $ fromString "NoConstructors (Cannot decode empty data type)"
3838

3939
instance decodeRepSum :: (DecodeRep a, DecodeRep b) => DecodeRep (Rep.Sum a b) where
4040
decodeRepWith e j = Rep.Inl <$> decodeRepWith e j <|> Rep.Inr <$> decodeRepWith e j
@@ -43,45 +43,45 @@ withTag ::
4343
Encoding ->
4444
Json ->
4545
String ->
46-
Either String
46+
Either JsonDecodeError
4747
{ tag :: String
48-
, decodingErr :: String -> String
48+
, decodingErr :: JsonDecodeError -> JsonDecodeError
4949
}
5050
withTag e j name = do
51-
let decodingErr msg = "When decoding a " <> name <> ": " <> msg
52-
jObj <- note (decodingErr "expected an object") (toObject j)
53-
jTag <- note (decodingErr $ "'" <> e.tagKey <> "' property is missing") (FO.lookup e.tagKey jObj)
54-
tag <- note (decodingErr $ "'" <> e.tagKey <> "' property is not a string") (toString jTag)
51+
let decodingErr = Named name
52+
jObj <- note (decodingErr $ TypeMismatch "Object") (toObject j)
53+
jTag <- note (decodingErr $ AtKey e.tagKey MissingValue) (FO.lookup e.tagKey jObj)
54+
tag <- note (decodingErr $ AtKey e.tagKey $ TypeMismatch "String") (toString jTag)
5555
when (tag /= name) $
56-
Left $ decodingErr $ "'" <> e.tagKey <> "' property has an incorrect value"
56+
Left $ decodingErr $ AtKey e.tagKey $ UnexpectedValue $ fromString tag
5757
pure {tag, decodingErr}
5858

5959
withTagAndValues ::
6060
Encoding ->
6161
Json ->
6262
String ->
63-
Either String
63+
Either JsonDecodeError
6464
{ tag :: String
6565
, values :: Json
66-
, decodingErr :: String -> String
66+
, decodingErr :: JsonDecodeError -> JsonDecodeError
6767
}
6868
withTagAndValues e j name = do
6969
{tag, decodingErr} <- withTag e j name
70-
jObj <- note (decodingErr "expected an object") (toObject j)
71-
values <- note (decodingErr $ "'" <> e.valuesKey <> "' property is missing") (FO.lookup e.valuesKey jObj)
70+
jObj <- note (decodingErr $ TypeMismatch "Object") (toObject j)
71+
values <- note (decodingErr $ AtKey e.valuesKey MissingValue) (FO.lookup e.valuesKey jObj)
7272
pure {tag, values, decodingErr}
7373

7474
construct ::
7575
forall e t s .
7676
DecodeRepArgs t =>
7777
Encoding ->
7878
Array Json ->
79-
(String -> e) ->
79+
(JsonDecodeError -> e) ->
8080
Either e (Rep.Constructor s t)
8181
construct e valuesArray decodingErr = do
8282
{init, rest} <- lmap decodingErr $ decodeRepArgs valuesArray
8383
when (rest /= []) $
84-
Left $ decodingErr $ "'" <> e.valuesKey <> "' property had too many values"
84+
Left $ decodingErr $ AtKey e.valuesKey $ UnexpectedValue (fromArray rest)
8585
pure $ Rep.Constructor init
8686

8787
instance decodeRepConstructorNoArgs :: IsSymbol name => DecodeRep (Rep.Constructor name Rep.NoArguments) where
@@ -97,18 +97,18 @@ instance decodeRepConstructorArg :: (IsSymbol name, DecodeJson a) => DecodeRep (
9797
if e.unwrapSingleArguments
9898
then construct e [values] decodingErr
9999
else do
100-
valuesArray <- note (decodingErr $ "'" <> e.valuesKey <> "' property is not an array") (toArray values)
100+
valuesArray <- note (decodingErr $ AtKey e.valuesKey $ TypeMismatch "Array") (toArray values)
101101
construct e valuesArray decodingErr
102102
else
103103
instance decodeRepConstructor :: (IsSymbol name, DecodeRepArgs a) => DecodeRep (Rep.Constructor name a) where
104104
decodeRepWith e j = do
105105
let name = reflectSymbol (SProxy :: SProxy name)
106106
{tag, values, decodingErr} <- withTagAndValues e j name
107-
valuesArray <- note (decodingErr $ "'" <> e.valuesKey <> "' property is not an array") (toArray values)
107+
valuesArray <- note (decodingErr $ AtKey e.valuesKey $ TypeMismatch "Array") (toArray values)
108108
construct e valuesArray decodingErr
109109

110110
class DecodeRepArgs r where
111-
decodeRepArgs :: Array Json -> Either String {init :: r, rest :: Array Json}
111+
decodeRepArgs :: Array Json -> Either JsonDecodeError {init :: r, rest :: Array Json}
112112

113113
instance decodeRepArgsNoArguments :: DecodeRepArgs Rep.NoArguments where
114114
decodeRepArgs js = Right {init: Rep.NoArguments, rest: js}
@@ -121,40 +121,40 @@ instance decodeRepArgsProduct :: (DecodeRepArgs a, DecodeRepArgs b) => DecodeRep
121121

122122
instance decodeRepArgsArgument :: (DecodeJson a) => DecodeRepArgs (Rep.Argument a) where
123123
decodeRepArgs js = do
124-
{head, tail} <- note "too few values were present" (uncons js)
124+
{head, tail} <- note (TypeMismatch "NonEmptyArray") (uncons js)
125125
{init: _, rest: tail} <<< Rep.Argument <$> decodeJson head
126126

127127
-- | Decode `Json` representation of a value which has a `Generic` type.
128-
genericDecodeJson :: forall a r. Rep.Generic a r => DecodeRep r => Json -> Either String a
128+
genericDecodeJson :: forall a r. Rep.Generic a r => DecodeRep r => Json -> Either JsonDecodeError a
129129
genericDecodeJson = genericDecodeJsonWith defaultEncoding
130130

131131
-- | Decode `Json` representation of a value which has a `Generic` type.
132132
-- | Takes a record for encoding settings.
133-
genericDecodeJsonWith :: forall a r. Rep.Generic a r => DecodeRep r => Encoding -> Json -> Either String a
133+
genericDecodeJsonWith :: forall a r. Rep.Generic a r => DecodeRep r => Encoding -> Json -> Either JsonDecodeError a
134134
genericDecodeJsonWith e = map Rep.to <<< decodeRepWith e
135135

136136
-- | A function for decoding `Generic` sum types using string literal representations.
137-
decodeLiteralSum :: forall a r. Rep.Generic a r => DecodeLiteral r => Json -> Either String a
137+
decodeLiteralSum :: forall a r. Rep.Generic a r => DecodeLiteral r => Json -> Either JsonDecodeError a
138138
decodeLiteralSum = decodeLiteralSumWithTransform identity
139139

140140
-- | A function for decoding `Generic` sum types using string literal representations.
141141
-- | Takes a function for transforming the tag name in encoding.
142-
decodeLiteralSumWithTransform :: forall a r. Rep.Generic a r => DecodeLiteral r => (String -> String) -> Json -> Either String a
142+
decodeLiteralSumWithTransform :: forall a r. Rep.Generic a r => DecodeLiteral r => (String -> String) -> Json -> Either JsonDecodeError a
143143
decodeLiteralSumWithTransform tagNameTransform = map Rep.to <<< decodeLiteral tagNameTransform
144144

145145
class DecodeLiteral r where
146-
decodeLiteral :: (String -> String) -> Json -> Either String r
146+
decodeLiteral :: (String -> String) -> Json -> Either JsonDecodeError r
147147

148148
instance decodeLiteralSumInst :: (DecodeLiteral a, DecodeLiteral b) => DecodeLiteral (Rep.Sum a b) where
149149
decodeLiteral tagNameTransform j = Rep.Inl <$> decodeLiteral tagNameTransform j <|> Rep.Inr <$> decodeLiteral tagNameTransform j
150150

151151
instance decodeLiteralConstructor :: (IsSymbol name) => DecodeLiteral (Rep.Constructor name (Rep.NoArguments)) where
152152
decodeLiteral tagNameTransform j = do
153153
let name = reflectSymbol (SProxy :: SProxy name)
154-
let decodingErr msg = "When decoding a " <> name <> ": " <> msg
155-
tag <- note (decodingErr "could not read string for constructor") (toString j)
154+
let decodingErr = Named name
155+
tag <- note (decodingErr $ TypeMismatch "String") (toString j)
156156
when (tag /= tagNameTransform name) $
157-
Left $ decodingErr "string literal " <> tag <> " had an incorrect value."
157+
Left $ decodingErr $ UnexpectedValue (fromString tag)
158158
pure $ Rep.Constructor (Rep.NoArguments)
159159

160160

test/Main.purs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@ module Test.Main
44

55
import Prelude
66

7-
import Effect (Effect)
8-
import Effect.Console (log)
9-
import Data.Argonaut.Core (stringify)
7+
import Data.Argonaut.Core (Json, stringify)
108
import Data.Argonaut.Decode.Class (class DecodeJson, decodeJson)
119
import Data.Argonaut.Decode.Generic.Rep (class DecodeLiteral, decodeLiteralSumWithTransform, genericDecodeJson, genericDecodeJsonWith)
1210
import Data.Argonaut.Encode.Class (class EncodeJson, encodeJson)
1311
import Data.Argonaut.Encode.Generic.Rep (class EncodeLiteral, encodeLiteralSumWithTransform, genericEncodeJson, genericEncodeJsonWith)
14-
import Data.Argonaut.Types.Generic.Rep (Encoding, defaultEncoding)
1512
import Data.Argonaut.Parser (jsonParser)
16-
import Data.Either (Either(..), fromRight)
13+
import Data.Argonaut.Types.Generic.Rep (Encoding, defaultEncoding)
14+
import Data.Either (Either(..), either, fromRight)
1715
import Data.Generic.Rep (class Generic)
1816
import Data.Generic.Rep.Show (genericShow)
1917
import Data.String (toLower, toUpper)
18+
import Effect (Effect)
19+
import Effect.Class (liftEffect)
20+
import Effect.Console (log)
21+
import Effect.Exception (throw)
2022
import Partial.Unsafe (unsafePartial)
2123
import Test.Assert (assert)
2224

@@ -90,6 +92,9 @@ instance encodeJsonIgnoreNullaryArgs :: EncodeJson IgnoreNullaryArgs where
9092
instance decodeJsonIgnoreNullaryArgs :: DecodeJson IgnoreNullaryArgs where
9193
decodeJson a = genericDecodeJson a
9294

95+
jsonParser' :: String -> Effect Json
96+
jsonParser' = either throw pure <<< jsonParser
97+
9398
main :: Effect Unit
9499
main = do
95100
example $ Either $ Left "foo"
@@ -113,7 +118,8 @@ main = do
113118

114119
example $ NA1 42
115120
example $ NA0
116-
assert $ (jsonParser """{"tag":"NA0"}""" >>= decodeJson) == Right NA0
121+
json <- jsonParser' """{"tag":"NA0"}"""
122+
assert $ (decodeJson json) == Right NA0
117123

118124
where
119125
example :: forall a. Show a => Eq a => EncodeJson a => DecodeJson a => a -> Effect Unit

0 commit comments

Comments
 (0)