Skip to content

Commit 96ec4b0

Browse files
Merge pull request #8 from CarstenKoenig/GenRepRowToList
updated to PureScript 0.12
2 parents 9a4b169 + 6402a87 commit 96ec4b0

File tree

8 files changed

+143
-157
lines changed

8 files changed

+143
-157
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ after_success:
1212
- >-
1313
test $TRAVIS_TAG &&
1414
echo $GITHUB_TOKEN | pulp login &&
15-
echo y | pulp publish --no-push
15+
echo y | pulp publish --no-push

bower.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
},
1717
"license": "MIT",
1818
"dependencies": {
19-
"purescript-generics": "^4.0.0",
20-
"purescript-argonaut-core": "^3.1.0",
21-
"purescript-argonaut-codecs": "^3.0.0",
22-
"purescript-generics-rep": "^5.1.0"
19+
"purescript-argonaut-core": "^4.0.1",
20+
"purescript-argonaut-codecs": "^4.0.1",
21+
"purescript-generics-rep": "^6.0.0",
22+
"purescript-record": "^1.0.0"
2323
},
2424
"devDependencies": {
25-
"purescript-assert": "^3.0.0",
26-
"purescript-console": "^3.0.0"
25+
"purescript-assert": "^4.0.0",
26+
"purescript-console": "^4.1.0"
2727
}
28-
}
28+
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
"test": "pulp test"
77
},
88
"devDependencies": {
9-
"pulp": "^11.0.0",
9+
"pulp": "^12.2.0",
1010
"purescript-psa": "^0.5.0",
11-
"purescript": "^0.11.1",
11+
"purescript": "^0.12.0",
1212
"rimraf": "^2.6.1"
1313
}
14-
}
14+
}

src/Data/Argonaut/Decode/Generic.purs

Lines changed: 0 additions & 58 deletions
This file was deleted.

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

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
module Data.Argonaut.Decode.Generic.Rep (
22
class DecodeRep,
33
class DecodeRepArgs,
4-
class DecodeRepFields,
4+
class DecodeRepRowList,
55
class DecodeLiteral,
66
decodeRep,
77
decodeRepArgs,
8-
decodeRepFields,
8+
decodeRepRowList,
99
genericDecodeJson,
1010
decodeLiteralSum,
1111
decodeLiteralSumWithTransform,
@@ -21,10 +21,16 @@ import Data.Array (uncons)
2121
import Data.Bifunctor (lmap)
2222
import Data.Either (Either(..))
2323
import Data.Generic.Rep as Rep
24-
import Data.Maybe (Maybe, maybe)
25-
import Data.StrMap as SM
24+
import Data.Maybe (Maybe(..), maybe)
2625
import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol)
26+
import Foreign.Object as FO
2727
import Partial.Unsafe (unsafeCrashWith)
28+
import Prim.Row as Row
29+
import Prim.RowList (class RowToList, Cons, Nil, kind RowList)
30+
import Prim.TypeError (class Fail, Text)
31+
import Record.Builder (Builder)
32+
import Record.Builder as Builder
33+
import Type.Data.RowList (RLProxy(..))
2834

2935
class DecodeRep r where
3036
decodeRep :: Json -> Either String r
@@ -40,11 +46,11 @@ instance decodeRepConstructor :: (IsSymbol name, DecodeRepArgs a) => DecodeRep (
4046
let name = reflectSymbol (SProxy :: SProxy name)
4147
let decodingErr msg = "When decoding a " <> name <> ": " <> msg
4248
jObj <- mFail (decodingErr "expected an object") (toObject j)
43-
jTag <- mFail (decodingErr "'tag' property is missing") (SM.lookup "tag" jObj)
49+
jTag <- mFail (decodingErr "'tag' property is missing") (FO.lookup "tag" jObj)
4450
tag <- mFail (decodingErr "'tag' property is not a string") (toString jTag)
4551
when (tag /= name) $
4652
Left $ decodingErr "'tag' property has an incorrect value"
47-
jValues <- mFail (decodingErr "'values' property is missing") (SM.lookup "values" jObj)
53+
jValues <- mFail (decodingErr "'values' property is missing") (FO.lookup "values" jObj)
4854
values <- mFail (decodingErr "'values' property is not an array") (toArray jValues)
4955
{init, rest} <- lmap decodingErr $ decodeRepArgs values
5056
when (rest /= []) $
@@ -63,28 +69,68 @@ instance decodeRepArgsProduct :: (DecodeRepArgs a, DecodeRepArgs b) => DecodeRep
6369
{init: b, rest: js''} <- decodeRepArgs js'
6470
pure {init: Rep.Product a b, rest: js''}
6571

66-
instance decodeRepArgsArgument :: (DecodeJson a) => DecodeRepArgs (Rep.Argument a) where
72+
instance decodeRepRecordArgument ::
73+
( RowToList row rl
74+
, DecodeRepRowList rl () row
75+
) => DecodeRepArgs (Rep.Argument (Record row)) where
6776
decodeRepArgs js = do
68-
{head, tail} <- mFail "too few values were present" (uncons js)
69-
{init: _, rest: tail} <<< Rep.Argument <$> decodeJson head
70-
71-
instance decodeRepArgsRec :: (DecodeRepFields fields) => DecodeRepArgs (Rep.Rec fields) where
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
7287
decodeRepArgs js = do
7388
{head, tail} <- mFail "too few values were present" (uncons js)
74-
jObj <- mFail "record is not encoded as an object" (toObject head)
75-
{init: _, rest: tail} <<< Rep.Rec <$> decodeRepFields jObj
76-
77-
class DecodeRepFields r where
78-
decodeRepFields :: SM.StrMap Json -> Either String r
89+
{init: _, rest: tail} <<< Rep.Argument <$> decodeJson head
7990

80-
instance decodeRepFieldsProduct :: (DecodeRepFields a, DecodeRepFields b) => DecodeRepFields (Rep.Product a b) where
81-
decodeRepFields js = Rep.Product <$> decodeRepFields js <*> decodeRepFields js
8291

83-
instance decodeRepFieldsField :: (IsSymbol field, DecodeJson a) => DecodeRepFields (Rep.Field field a) where
84-
decodeRepFields js = do
85-
let name = reflectSymbol (SProxy :: SProxy field)
86-
value <- mFail ("the field '" <> name <> "' is not present") (SM.lookup name js)
87-
Rep.Field <$> decodeJson value
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
88134

89135
-- | Decode `Json` representation of a value which has a `Generic` type.
90136
genericDecodeJson :: forall a r. Rep.Generic a r => DecodeRep r => Json -> Either String a
@@ -95,7 +141,7 @@ mFail msg = maybe (Left msg) Right
95141

96142
-- | A function for decoding `Generic` sum types using string literal representations
97143
decodeLiteralSum :: forall a r. Rep.Generic a r => DecodeLiteral r => Json -> Either String a
98-
decodeLiteralSum = decodeLiteralSumWithTransform id
144+
decodeLiteralSum = decodeLiteralSumWithTransform identity
99145

100146
-- | A function for decoding `Generic` sum types using string literal representations
101147
-- | Takes a function for transforming the tag name in encoding
@@ -117,7 +163,9 @@ instance decodeLiteralConstructor :: (IsSymbol name) => DecodeLiteral (Rep.Const
117163
Left $ decodingErr "string literal " <> tag <> " had an incorrect value."
118164
pure $ Rep.Constructor (Rep.NoArguments)
119165

120-
type FailMessage = """`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."""
166+
167+
type FailMessage =
168+
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."
121169

122170
instance decodeLiteralConstructorCannotTakeProduct
123171
:: Fail FailMessage

src/Data/Argonaut/Encode/Generic.purs

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)