1
1
module Data.Argonaut.Decode.Generic.Rep (
2
2
class DecodeRep ,
3
3
class DecodeRepArgs ,
4
- class DecodeRepRowList ,
5
4
class DecodeLiteral ,
6
- decodeRep ,
5
+ decodeRepWith ,
7
6
decodeRepArgs ,
8
- decodeRepRowList ,
9
7
genericDecodeJson ,
8
+ genericDecodeJsonWith ,
10
9
decodeLiteralSum ,
11
10
decodeLiteralSumWithTransform ,
12
11
decodeLiteral
13
12
) where
14
13
15
14
import Prelude
15
+ import Data.Argonaut.Types.Generic.Rep (Encoding , defaultEncoding )
16
16
17
17
import Control.Alt ((<|>))
18
18
import Data.Argonaut.Core (Json , toArray , toObject , toString )
@@ -21,40 +21,35 @@ import Data.Array (uncons)
21
21
import Data.Bifunctor (lmap )
22
22
import Data.Either (Either (..))
23
23
import Data.Generic.Rep as Rep
24
- import Data.Maybe (Maybe (..) , maybe )
24
+ import Data.Maybe (Maybe , maybe )
25
25
import Data.Symbol (class IsSymbol , SProxy (..), reflectSymbol )
26
26
import Foreign.Object as FO
27
27
import Partial.Unsafe (unsafeCrashWith )
28
- import Prim.Row as Row
29
- import Prim.RowList (class RowToList , Cons , Nil , kind RowList )
30
28
import Prim.TypeError (class Fail , Text )
31
- import Record.Builder (Builder )
32
- import Record.Builder as Builder
33
- import Type.Data.RowList (RLProxy (..))
34
29
35
30
class DecodeRep r where
36
- decodeRep :: Json -> Either String r
31
+ decodeRepWith :: Encoding -> Json -> Either String r
37
32
38
33
instance decodeRepNoConstructors :: DecodeRep Rep.NoConstructors where
39
- decodeRep _ = Left " Cannot decode empty data type"
34
+ decodeRepWith e _ = Left " Cannot decode empty data type"
40
35
41
36
instance decodeRepSum :: (DecodeRep a , DecodeRep b ) => DecodeRep (Rep.Sum a b ) where
42
- decodeRep j = Rep.Inl <$> decodeRep j <|> Rep.Inr <$> decodeRep j
37
+ decodeRepWith e j = Rep.Inl <$> decodeRepWith e j <|> Rep.Inr <$> decodeRepWith e j
43
38
44
39
instance decodeRepConstructor :: (IsSymbol name , DecodeRepArgs a ) => DecodeRep (Rep.Constructor name a ) where
45
- decodeRep j = do
40
+ decodeRepWith e j = do
46
41
let name = reflectSymbol (SProxy :: SProxy name )
47
42
let decodingErr msg = " When decoding a " <> name <> " : " <> msg
48
43
jObj <- mFail (decodingErr " expected an object" ) (toObject j)
49
- jTag <- mFail (decodingErr " 'tag ' property is missing" ) (FO .lookup " tag " jObj)
50
- tag <- mFail (decodingErr " 'tag ' property is not a string" ) (toString jTag)
44
+ jTag <- mFail (decodingErr $ " ' " <> e.tagKey <> " ' property is missing" ) (FO .lookup e.tagKey jObj)
45
+ tag <- mFail (decodingErr $ " ' " <> e.tagKey <> " ' property is not a string" ) (toString jTag)
51
46
when (tag /= name) $
52
- Left $ decodingErr " 'tag ' property has an incorrect value"
53
- jValues <- mFail (decodingErr " 'values ' property is missing" ) (FO .lookup " values " jObj)
54
- values <- mFail (decodingErr " 'values ' property is not an array" ) (toArray jValues)
47
+ Left $ decodingErr $ " ' " <> e.tagKey <> " ' property has an incorrect value"
48
+ jValues <- mFail (decodingErr $ " ' " <> e.valuesKey <> " ' property is missing" ) (FO .lookup e.valuesKey jObj)
49
+ values <- mFail (decodingErr $ " ' " <> e.valuesKey <> " ' property is not an array" ) (toArray jValues)
55
50
{init, rest} <- lmap decodingErr $ decodeRepArgs values
56
51
when (rest /= [] ) $
57
- Left $ decodingErr " 'values ' property had too many values"
52
+ Left $ decodingErr $ " ' " <> e.valuesKey <> " ' property had too many values"
58
53
pure $ Rep.Constructor init
59
54
60
55
class DecodeRepArgs r where
@@ -69,72 +64,18 @@ instance decodeRepArgsProduct :: (DecodeRepArgs a, DecodeRepArgs b) => DecodeRep
69
64
{init: b, rest: js''} <- decodeRepArgs js'
70
65
pure {init: Rep.Product a b, rest: js''}
71
66
72
- instance decodeRepRecordArgument ::
73
- ( RowToList row rl
74
- , DecodeRepRowList rl () row
75
- ) => DecodeRepArgs (Rep.Argument (Record row )) where
76
- decodeRepArgs js = do
77
- {head, tail} <- mFail " to few values were present" (uncons js)
78
- obj <- mFail " no json object" (toObject head)
79
- steps <- decodeRepRowList rlp obj
80
- let arg = Rep.Argument $ Builder .build steps {}
81
- pure {init: arg, rest: tail}
82
- where
83
- rlp :: RLProxy rl
84
- rlp = RLProxy
85
-
86
- else instance decodeRepArgsArgument :: (DecodeJson a ) => DecodeRepArgs (Rep.Argument a ) where
67
+ instance decodeRepArgsArgument :: (DecodeJson a ) => DecodeRepArgs (Rep.Argument a ) where
87
68
decodeRepArgs js = do
88
69
{head, tail} <- mFail " too few values were present" (uncons js)
89
70
{init: _, rest: tail} <<< Rep.Argument <$> decodeJson head
90
71
91
-
92
- -- | a `DecodeRepRowList` represents a relation between a `RowList` and a record you
93
- -- | can build from it by deserializing it's fields from a JSON `Object`
94
- -- |
95
- -- | this one is strictly internal to help out `decodeRepRecordArgument` handling records
96
- -- |
97
- -- | a `RowList` on the type level is very similar to a *cons-list* on the value level
98
- -- | so the two instances handle all possible `RowList`s
99
- -- |
100
- -- | the idea is to use `Builder` to convert a `RowList` into a record at the type-level
101
- -- | and have `decodeRepRowList` as witness on the value level that will try to decode
102
- -- | JSON in to the resulting record value
103
- -- |
104
- -- | `from` and `to` are two helper types - using these `decodeRepRowListCons` can
105
- -- | recursively create `Builder`-steps and make sure that every *symbol* in `rl`
106
- -- | can only occur once (the fields in the records must be unique)
107
- -- | (see `Row.Lacks`)
108
- class DecodeRepRowList (rl :: RowList ) (from :: #Type ) (to :: #Type ) | rl -> from to where
109
- decodeRepRowList :: forall g . g rl -> FO.Object Json -> Either String (Builder (Record from ) (Record to ))
110
-
111
- instance decodeRepRowListNil :: DecodeRepRowList Nil () () where
112
- decodeRepRowList _ _ = pure identity
113
-
114
- instance decodeRepRowListCons ::
115
- ( DecodeJson ty
116
- , IsSymbol name
117
- , DecodeRepRowList tail from from'
118
- , Row.Lacks name from'
119
- , Row.Cons name ty from' to
120
- ) => DecodeRepRowList (Cons name ty tail ) from to where
121
- decodeRepRowList _ obj = do
122
- value :: ty <- (error $ FO .lookup name obj) >>= decodeJson
123
- rest <- decodeRepRowList tailp obj
124
- let
125
- first :: Builder (Record from' ) (Record to )
126
- first = Builder .insert namep value
127
- pure $ first <<< rest
128
- where
129
- namep = SProxy :: SProxy name
130
- tailp = RLProxy :: RLProxy tail
131
- name = reflectSymbol namep
132
- error Nothing = Left (" error while decoding field " <> name)
133
- error (Just a) = Right a
134
-
135
72
-- | Decode `Json` representation of a value which has a `Generic` type.
136
73
genericDecodeJson :: forall a r . Rep.Generic a r => DecodeRep r => Json -> Either String a
137
- genericDecodeJson = map Rep .to <<< decodeRep
74
+ genericDecodeJson = genericDecodeJsonWith defaultEncoding
75
+
76
+ -- | Decode `Json` representation of a value which has a `Generic` type.
77
+ genericDecodeJsonWith :: forall a r . Rep.Generic a r => DecodeRep r => Encoding -> Json -> Either String a
78
+ genericDecodeJsonWith e = map Rep .to <<< decodeRepWith e
138
79
139
80
mFail :: forall a . String -> Maybe a -> Either String a
140
81
mFail msg = maybe (Left msg) Right
@@ -164,7 +105,7 @@ instance decodeLiteralConstructor :: (IsSymbol name) => DecodeLiteral (Rep.Const
164
105
pure $ Rep.Constructor (Rep.NoArguments )
165
106
166
107
167
- type FailMessage =
108
+ type FailMessage =
168
109
Text " `decodeLiteralSum` can only be used with sum types, where all of the constructors are nullary. This is because a string literal cannot be encoded into a product type."
169
110
170
111
instance decodeLiteralConstructorCannotTakeProduct
0 commit comments