@@ -43,10 +43,14 @@ module Data.Aeson.Types.Internal
43
43
, parse
44
44
, parseEither
45
45
, parseMaybe
46
+ , liftP2
47
+ , (<*>+)
46
48
, modifyFailure
47
49
, parserThrowError
48
50
, parserCatchError
51
+ , parserCatchErrors
49
52
, formatError
53
+ , formatErrors
50
54
, (<?>)
51
55
-- * Constructors and accessors
52
56
, object
@@ -87,6 +91,7 @@ import Data.Foldable (foldl')
87
91
import Data.HashMap.Strict (HashMap )
88
92
import Data.Hashable (Hashable (.. ))
89
93
import Data.List (intercalate )
94
+ import Data.List.NonEmpty (NonEmpty ((:|) ))
90
95
import Data.Scientific (Scientific )
91
96
import Data.Semigroup (Semigroup ((<>) ))
92
97
import Data.String (IsString (.. ))
@@ -98,6 +103,7 @@ import Data.Vector (Vector)
98
103
import GHC.Generics (Generic )
99
104
import qualified Control.Monad.Fail as Fail
100
105
import qualified Data.HashMap.Strict as H
106
+ import qualified Data.List.NonEmpty as NonEmpty
101
107
import qualified Data.Scientific as S
102
108
import qualified Data.Vector as V
103
109
import qualified Language.Haskell.TH.Syntax as TH
@@ -118,7 +124,7 @@ data JSONPathElement = Key Text
118
124
type JSONPath = [JSONPathElement ]
119
125
120
126
-- | The internal result of running a 'Parser'.
121
- data IResult a = IError JSONPath String
127
+ data IResult a = IError ( NonEmpty ( JSONPath , String ))
122
128
| ISuccess a
123
129
deriving (Eq , Show , Typeable )
124
130
@@ -133,15 +139,15 @@ instance NFData JSONPathElement where
133
139
134
140
instance (NFData a ) => NFData (IResult a ) where
135
141
rnf (ISuccess a) = rnf a
136
- rnf (IError path err) = rnf path `seq` rnf err
142
+ rnf (IError err) = rnf err
137
143
138
144
instance (NFData a ) => NFData (Result a ) where
139
145
rnf (Success a) = rnf a
140
146
rnf (Error err) = rnf err
141
147
142
148
instance Functor IResult where
143
- fmap f (ISuccess a) = ISuccess (f a)
144
- fmap _ (IError path err) = IError path err
149
+ fmap f (ISuccess a) = ISuccess (f a)
150
+ fmap _ (IError err) = IError err
145
151
{-# INLINE fmap #-}
146
152
147
153
instance Functor Result where
@@ -153,15 +159,15 @@ instance Monad IResult where
153
159
return = pure
154
160
{-# INLINE return #-}
155
161
156
- ISuccess a >>= k = k a
157
- IError path err >>= _ = IError path err
162
+ ISuccess a >>= k = k a
163
+ IError err >>= _ = IError err
158
164
{-# INLINE (>>=) #-}
159
165
160
166
fail = Fail. fail
161
167
{-# INLINE fail #-}
162
168
163
169
instance Fail. MonadFail IResult where
164
- fail err = IError [] err
170
+ fail err = IError (( [] , err) :| [] )
165
171
{-# INLINE fail #-}
166
172
167
173
instance Monad Result where
@@ -238,11 +244,11 @@ instance Monoid (Result a) where
238
244
{-# INLINE mappend #-}
239
245
240
246
instance Foldable IResult where
241
- foldMap _ (IError _ _) = mempty
247
+ foldMap _ (IError _) = mempty
242
248
foldMap f (ISuccess y) = f y
243
249
{-# INLINE foldMap #-}
244
250
245
- foldr _ z (IError _ _) = z
251
+ foldr _ z (IError _) = z
246
252
foldr f z (ISuccess y) = f y z
247
253
{-# INLINE foldr #-}
248
254
@@ -256,8 +262,8 @@ instance Foldable Result where
256
262
{-# INLINE foldr #-}
257
263
258
264
instance Traversable IResult where
259
- traverse _ (IError path err) = pure (IError path err)
260
- traverse f (ISuccess a) = ISuccess <$> f a
265
+ traverse _ (IError err) = pure (IError err)
266
+ traverse f (ISuccess a) = ISuccess <$> f a
261
267
{-# INLINE traverse #-}
262
268
263
269
instance Traversable Result where
@@ -266,7 +272,7 @@ instance Traversable Result where
266
272
{-# INLINE traverse #-}
267
273
268
274
-- | Failure continuation.
269
- type Failure f r = JSONPath -> String -> f r
275
+ type Failure f r = NonEmpty ( JSONPath , String ) -> f r
270
276
-- | Success continuation.
271
277
type Success a f r = a -> f r
272
278
@@ -289,7 +295,7 @@ instance Monad Parser where
289
295
{-# INLINE fail #-}
290
296
291
297
instance Fail. MonadFail Parser where
292
- fail msg = Parser $ \ path kf _ks -> kf (reverse path) msg
298
+ fail msg = Parser $ \ path kf _ks -> kf (( reverse path, msg) :| [] )
293
299
{-# INLINE fail #-}
294
300
295
301
instance Functor Parser where
@@ -309,10 +315,11 @@ instance Alternative Parser where
309
315
(<|>) = mplus
310
316
{-# INLINE (<|>) #-}
311
317
318
+ {- TODO accumulate errors -}
312
319
instance MonadPlus Parser where
313
320
mzero = fail " mzero"
314
321
{-# INLINE mzero #-}
315
- mplus a b = Parser $ \ path kf ks -> let kf' _ _ = runParser b path kf ks
322
+ mplus a b = Parser $ \ path kf ks -> let kf' _ = runParser b path kf ks
316
323
in runParser a path kf' ks
317
324
{-# INLINE mplus #-}
318
325
@@ -333,6 +340,22 @@ apP d e = do
333
340
return (b a)
334
341
{-# INLINE apP #-}
335
342
343
+ -- | A variant of 'Control.Applicative.liftA2' that lazily accumulates errors
344
+ -- from both subparsers.
345
+ liftP2 :: (a -> b -> c ) -> Parser a -> Parser b -> Parser c
346
+ liftP2 f pa pb = Parser $ \ path kf ks ->
347
+ runParser pa path
348
+ (\ (e :| es) -> kf (e :| es ++ runParser pb path NonEmpty. toList (const [] )))
349
+ (\ a -> runParser pb path kf (\ b -> ks (f a b)))
350
+ {-# INLINE liftP2 #-}
351
+
352
+ infixl 4 <*>+
353
+
354
+ -- | A variant of ('<*>') that lazily accumulates errors from both subparsers.
355
+ (<*>+) :: Parser (a -> b ) -> Parser a -> Parser b
356
+ (<*>+) = liftP2 id
357
+ {-# INLINE (<*>+) #-}
358
+
336
359
-- | A JSON \"object\" (key\/value map).
337
360
type Object = HashMap Text Value
338
361
@@ -423,7 +446,7 @@ emptyObject = Object H.empty
423
446
424
447
-- | Run a 'Parser'.
425
448
parse :: (a -> Parser b ) -> a -> Result b
426
- parse m v = runParser (m v) [] (const Error ) Success
449
+ parse m v = runParser (m v) [] (Error . snd . NonEmpty. head ) Success
427
450
{-# INLINE parse #-}
428
451
429
452
-- | Run a 'Parser'.
@@ -433,14 +456,14 @@ iparse m v = runParser (m v) [] IError ISuccess
433
456
434
457
-- | Run a 'Parser' with a 'Maybe' result type.
435
458
parseMaybe :: (a -> Parser b ) -> a -> Maybe b
436
- parseMaybe m v = runParser (m v) [] (\ _ _ -> Nothing ) Just
459
+ parseMaybe m v = runParser (m v) [] (const Nothing ) Just
437
460
{-# INLINE parseMaybe #-}
438
461
439
462
-- | Run a 'Parser' with an 'Either' result type. If the parse fails,
440
463
-- the 'Left' payload will contain an error message.
441
464
parseEither :: (a -> Parser b ) -> a -> Either String b
442
465
parseEither m v = runParser (m v) [] onError Right
443
- where onError path msg = Left (formatError path msg )
466
+ where onError (( path, err) :| _) = Left (formatError path err )
444
467
{-# INLINE parseEither #-}
445
468
446
469
-- | Annotate an error message with a
@@ -471,6 +494,10 @@ formatError path msg = "Error in " ++ format "$" path ++ ": " ++ msg
471
494
escapeChar ' \\ ' = " \\\\ "
472
495
escapeChar c = [c]
473
496
497
+ -- | Annotate a list of error messages.
498
+ formatErrors :: Functor f => f (JSONPath , String ) -> f String
499
+ formatErrors = fmap (uncurry formatError)
500
+
474
501
-- | A key\/value pair for an 'Object'.
475
502
type Pair = (Text , Value )
476
503
@@ -510,21 +537,26 @@ p <?> pathElem = Parser $ \path kf ks -> runParser p (pathElem:path) kf ks
510
537
-- Since 0.6.2.0
511
538
modifyFailure :: (String -> String ) -> Parser a -> Parser a
512
539
modifyFailure f (Parser p) = Parser $ \ path kf ks ->
513
- p path (\ p' m -> kf p' ( f m)) ks
540
+ p path (\ m -> kf (( fmap . fmap ) f m)) ks
514
541
515
542
-- | Throw a parser error with an additional path.
516
543
--
517
544
-- @since 1.2.1.0
518
545
parserThrowError :: JSONPath -> String -> Parser a
519
546
parserThrowError path' msg = Parser $ \ path kf _ks ->
520
- kf (reverse path ++ path') msg
547
+ kf (( reverse path ++ path', msg) :| [] )
521
548
522
549
-- | A handler function to handle previous errors and return to normal execution.
523
550
--
524
551
-- @since 1.2.1.0
525
552
parserCatchError :: Parser a -> (JSONPath -> String -> Parser a ) -> Parser a
526
- parserCatchError (Parser p) handler = Parser $ \ path kf ks ->
527
- p path (\ e msg -> runParser (handler e msg) path kf ks) ks
553
+ parserCatchError p handler = parserCatchErrors p (\ ((e, msg) :| _) -> handler e msg)
554
+
555
+ -- | A handler function to handle multiple previous errors and return to normal
556
+ -- execution.
557
+ parserCatchErrors :: Parser a -> (NonEmpty (JSONPath , String ) -> Parser a ) -> Parser a
558
+ parserCatchErrors (Parser p) handler = Parser $ \ path kf ks ->
559
+ p path (\ es -> runParser (handler es) path kf ks) ks
528
560
529
561
--------------------------------------------------------------------------------
530
562
-- Generic and TH encoding configuration
0 commit comments