1
1
{-# LANGUAGE PolyKinds #-}
2
2
3
3
module Cardano.Wallet.API.V1.Generic
4
- ( gtoJsend
5
- , gparseJsend
6
- , gconsNames
4
+ ( gconsNames
7
5
, gconsName
8
6
) where
9
7
10
8
import Universum hiding (All , Generic )
11
9
12
- import Data.Aeson
13
- import Data.Aeson.Types (Parser )
14
10
import Data.List ((!!) )
15
11
import Generics.SOP
16
- import Generics.SOP.JSON (JsonInfo (.. ), JsonOptions (.. ), Tag (.. ),
17
- defaultJsonOptions )
18
-
19
- import Cardano.Wallet.API.Response.JSend (ResponseStatus (.. ))
20
- import Cardano.Wallet.Util (mkJsonKey )
21
- import Pos.Util.Util (aesonError )
22
-
23
- import qualified Data.HashMap.Strict as HM
24
- import qualified Data.Vector as V
25
-
26
- --
27
- -- Helper proxies
28
- --
29
-
30
- pt :: Proxy ToJSON
31
- pt = Proxy
32
-
33
- allpt :: Proxy (All ToJSON )
34
- allpt = Proxy
35
-
36
- pf :: Proxy FromJSON
37
- pf = Proxy
38
-
39
- allpf :: Proxy (All FromJSON )
40
- allpf = Proxy
41
12
42
13
--
43
- -- JSON encoding/decoding
14
+ -- Misc
44
15
--
45
16
46
- -- | Generic method which makes JSON `Value` from a Haskell value in
47
- -- JSend format.
48
- gtoJsend
49
- :: forall a . (Generic a , HasDatatypeInfo a , All2 ToJSON (Code a ))
50
- => ResponseStatus -> a -> Value
51
- gtoJsend rs a =
52
- hcollapse $
53
- hcliftA2 allpt (gtoJsend' rs)
54
- (jsendInfo (Proxy :: Proxy a ) jsendOptions)
55
- (unSOP $ from a)
56
-
57
- -- | Our custom naming options
58
- jsendOptions :: JsonOptions
59
- jsendOptions = defaultJsonOptions
60
- { jsonFieldName = const mkJsonKey
61
- }
62
-
63
- -- | Slightly modified version compared to Generics.SOP.JSON, we also tag
64
- -- single-constructor (ADT with one constructor and newtype) because we
65
- -- rely on that information to wrap the corresponding json in a jsend payload.
66
- jsendInfo :: forall a . (HasDatatypeInfo a , SListI (Code a ))
67
- => Proxy a -> JsonOptions -> NP JsonInfo (Code a )
68
- jsendInfo pa opts =
69
- case datatypeInfo pa of
70
- Newtype _ t _ -> JsonOne (Tag $ jsonTagName opts t) :* Nil
71
- ADT _ n cs -> hliftA (jsonInfoFor opts n (Tag . jsonTagName opts)) cs
72
-
73
- -- Extracted from Generics.SOP.JSON
74
- jsonInfoFor :: forall xs . JsonOptions -> DatatypeName -> (ConstructorName -> Tag ) -> ConstructorInfo xs -> JsonInfo xs
75
- jsonInfoFor _ _ tag (Infix n _ _) = JsonMultiple (tag n)
76
- jsonInfoFor _ _ tag (Constructor n) =
77
- case shape :: Shape xs of
78
- ShapeNil -> JsonZero n
79
- ShapeCons ShapeNil -> JsonOne (tag n)
80
- _ -> JsonMultiple (tag n)
81
- jsonInfoFor opts d tag (Record n fields) =
82
- JsonRecord (tag n) (hliftA jfieldName fields)
83
- where
84
- jfieldName :: FieldInfo a -> K String a
85
- jfieldName (FieldInfo name) = K (jsonFieldName opts d name)
86
-
87
- gtoJsend'
88
- :: All ToJSON xs
89
- => ResponseStatus -> JsonInfo xs -> NP I xs -> K Value xs
90
- gtoJsend' rs (JsonZero n) Nil =
91
- jsendValue rs (Tag n) (Object mempty )
92
- gtoJsend' rs (JsonOne tag) (I a :* Nil ) =
93
- jsendValue rs tag (toJSON a)
94
- gtoJsend' rs (JsonMultiple tag) cs =
95
- jsendValue rs tag . Array . V. fromList . hcollapse $
96
- hcliftA pt (K . toJSON . unI) cs
97
- gtoJsend' rs (JsonRecord tag fields) cs =
98
- jsendValue rs tag . Object . HM. fromList . hcollapse $
99
- hcliftA2 pt (\ (K field) (I a) -> K (toText field, toJSON a)) fields cs
100
-
101
- -- | Generic method which parses a Haskell value from given `Value`.
102
- gparseJsend
103
- :: forall a . (Generic a , HasDatatypeInfo a , All2 FromJSON (Code a ))
104
- => Value -> Parser a
105
- gparseJsend v = to <$> gparseJsend' v (jsendInfo (Proxy :: Proxy a ) jsendOptions)
106
-
107
- gparseJsend'
108
- :: forall (xss :: [[* ]]). All2 FromJSON xss
109
- => Value -> NP JsonInfo xss -> Parser (SOP I xss )
110
- gparseJsend' v infos = asum . hcollapse $
111
- hcliftA2 allpf (parseJsendConstructor v) infos
112
- (injections :: NP (Injection (NP I ) xss ) xss )
113
-
114
- parseJsendConstructor
115
- :: forall (xss :: [[* ]]) (xs :: [* ]). All FromJSON xs
116
- => Value -> JsonInfo xs -> Injection (NP I ) xss xs -> K (Parser (SOP I xss )) xs
117
- parseJsendConstructor v info (Fn inj) = K $ do
118
- vals <- parseJsendValues info v
119
- return $ SOP $ unK (inj vals)
17
+ -- | Get all constructors names available of an ADT
18
+ gconsNames
19
+ :: forall a . (HasDatatypeInfo a , SListI (Code a ))
20
+ => Proxy a -> [Text ]
21
+ gconsNames =
22
+ map toText . hcollapse . hliftA (K . constructorName) . gconsInfos
120
23
121
- parseJsendValues
122
- :: forall (xs :: [* ]). All FromJSON xs
123
- => JsonInfo xs -> Value -> Parser (NP I xs )
124
- parseJsendValues (JsonZero n) =
125
- unJsendValue (Tag n) $
126
- const $ return Nil
127
- parseJsendValues (JsonOne tag) =
128
- unJsendValue tag $ \ o -> do
129
- v <- parseJSON o
130
- return $ I v :* Nil
131
- parseJsendValues (JsonMultiple tag) =
132
- unJsendValue tag $
133
- withArray " Array" $ \ arr ->
134
- case fromList (V. toList arr) of
135
- Nothing -> aesonError " Too few values!"
136
- Just vals ->
137
- let mkVal :: FromJSON a => K Value a -> Parser a
138
- mkVal = parseJSON . unK
139
- in hsequence $ hcliftA pf mkVal vals
140
- parseJsendValues (JsonRecord tag fields) =
141
- unJsendValue tag $
142
- withObject " Object" $ \ o ->
143
- let getField :: FromJSON a => K String a -> Parser a
144
- getField (K name) = o .: toText name
145
- in hsequence $ hcliftA pf getField fields
146
24
147
- -- | Helper function which makes a JSON value in JSend format
148
- -- from a constructor tag and object with constructor's arguments
149
- jsendValue :: ResponseStatus -> Tag -> Value -> K Value a
150
- jsendValue _ NoTag v = K v
151
- jsendValue rs (Tag t) v = K $ Object $
152
- HM. fromList [ (" message" , String $ toText t)
153
- , (" diagnostic" , v)
154
- , (" status" , toJSON rs)
155
- ]
25
+ -- | Get the ADT constructor's name of the given value
26
+ gconsName
27
+ :: forall a . (Generic a , HasDatatypeInfo a )
28
+ => a -> Text
29
+ gconsName a =
30
+ gconsNames (Proxy @ a ) !! hindex (from a)
156
31
157
- -- | Helper function to parse value in JSend format if desired constructor
158
- -- is known.
159
- unJsendValue :: Tag -> (Value -> Parser a ) -> Value -> Parser a
160
- unJsendValue NoTag f = f
161
- unJsendValue (Tag n) f = withObject (" Expected JSend object with message `" <> n <> " `" ) $
162
- \ o -> do
163
- msg <- o .: " message"
164
- guard $ n == msg
165
- val <- o .: " diagnostic"
166
- f val
167
32
168
33
--
169
- -- Misc
34
+ -- INTERNALS
170
35
--
171
36
172
37
gconsInfos
@@ -176,14 +41,3 @@ gconsInfos pa = case datatypeInfo pa of
176
41
Newtype _ _ conInfo -> conInfo :* Nil
177
42
ADT _ _ consInfo -> consInfo
178
43
179
- gconsNames
180
- :: forall a . (HasDatatypeInfo a , SListI (Code a ))
181
- => Proxy a -> [Text ]
182
- gconsNames =
183
- map toText . hcollapse . hliftA (K . constructorName) . gconsInfos
184
-
185
- gconsName
186
- :: forall a . (Generic a , HasDatatypeInfo a )
187
- => a -> Text
188
- gconsName a =
189
- gconsNames (Proxy @ a ) !! hindex (from a)
0 commit comments