packages feed

jsonpatch 0.1.0.0 → 0.2.0.0

raw patch · 12 files changed

+250/−219 lines, 12 filesdep +optics-coredep −mtldep −opticsPVP ok

version bump matches the API change (PVP)

Dependencies added: optics-core

Dependencies removed: mtl, optics

API changes (from Hackage documentation)

- Data.JSON.Pointer: PointerEmpty :: Pointer
- Data.JSON.Pointer: PointerPath :: [Token] -> Token -> Pointer
- Data.JSON.Pointer: PointerPathEnd :: [Token] -> Pointer
- Data.JSON.Pointer: data Pointer
- Data.JSON.Pointer.Token: tokenP :: Parser Token
- Data.JSON.Pointer.Token: tokensL :: [Token] -> AffineTraversal' Value Value
- Data.JSON.Pointer.Token: tokensToString :: [Token] -> String
- Data.JSON.Pointer.Token: tokensToText :: [Token] -> Text
+ Data.Aeson.Optics.Ext: atEnd :: AffineTraversal' Value (Maybe Value)
+ Data.JSON.Patch.Prelude: (!!) :: HasCallStack => [a] -> Int -> a
+ Data.JSON.Patch.Prelude: ($!) :: forall (r :: RuntimeRep) a (b :: TYPE r). (a -> b) -> a -> b
+ Data.JSON.Patch.Prelude: ($) :: forall (r :: RuntimeRep) a (b :: TYPE r). (a -> b) -> a -> b
+ Data.JSON.Patch.Prelude: (&&) :: Bool -> Bool -> Bool
+ Data.JSON.Patch.Prelude: (*) :: Num a => a -> a -> a
+ Data.JSON.Patch.Prelude: (**) :: Floating a => a -> a -> a
+ Data.JSON.Patch.Prelude: (*>) :: Applicative f => f a -> f b -> f b
+ Data.JSON.Patch.Prelude: (+) :: Num a => a -> a -> a
+ Data.JSON.Patch.Prelude: (++) :: [a] -> [a] -> [a]
+ Data.JSON.Patch.Prelude: (-) :: Num a => a -> a -> a
+ Data.JSON.Patch.Prelude: (.) :: (b -> c) -> (a -> b) -> a -> c
+ Data.JSON.Patch.Prelude: (/) :: Fractional a => a -> a -> a
+ Data.JSON.Patch.Prelude: (/=) :: Eq a => a -> a -> Bool
+ Data.JSON.Patch.Prelude: (<$) :: Functor f => a -> f b -> f a
+ Data.JSON.Patch.Prelude: (<$>) :: Functor f => (a -> b) -> f a -> f b
+ Data.JSON.Patch.Prelude: (<) :: Ord a => a -> a -> Bool
+ Data.JSON.Patch.Prelude: (<*) :: Applicative f => f a -> f b -> f a
+ Data.JSON.Patch.Prelude: (<*>) :: Applicative f => f (a -> b) -> f a -> f b
+ Data.JSON.Patch.Prelude: (<=) :: Ord a => a -> a -> Bool
+ Data.JSON.Patch.Prelude: (<>) :: Semigroup a => a -> a -> a
+ Data.JSON.Patch.Prelude: (<|>) :: Alternative f => f a -> f a -> f a
+ Data.JSON.Patch.Prelude: (=<<) :: Monad m => (a -> m b) -> m a -> m b
+ Data.JSON.Patch.Prelude: (==) :: Eq a => a -> a -> Bool
+ Data.JSON.Patch.Prelude: (>) :: Ord a => a -> a -> Bool
+ Data.JSON.Patch.Prelude: (>=) :: Ord a => a -> a -> Bool
+ Data.JSON.Patch.Prelude: (>>) :: Monad m => m a -> m b -> m b
+ Data.JSON.Patch.Prelude: (>>=) :: Monad m => m a -> (a -> m b) -> m b
+ Data.JSON.Patch.Prelude: (^) :: (Num a, Integral b) => a -> b -> a
+ Data.JSON.Patch.Prelude: (^^) :: (Fractional a, Integral b) => a -> b -> a
+ Data.JSON.Patch.Prelude: (||) :: Bool -> Bool -> Bool
+ Data.JSON.Patch.Prelude: EQ :: Ordering
+ Data.JSON.Patch.Prelude: False :: Bool
+ Data.JSON.Patch.Prelude: GT :: Ordering
+ Data.JSON.Patch.Prelude: Just :: a -> Maybe a
+ Data.JSON.Patch.Prelude: LT :: Ordering
+ Data.JSON.Patch.Prelude: Left :: a -> Either a b
+ Data.JSON.Patch.Prelude: Nothing :: Maybe a
+ Data.JSON.Patch.Prelude: Right :: b -> Either a b
+ Data.JSON.Patch.Prelude: True :: Bool
+ Data.JSON.Patch.Prelude: abs :: Num a => a -> a
+ Data.JSON.Patch.Prelude: acos :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: acosh :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: all :: Foldable t => (a -> Bool) -> t a -> Bool
+ Data.JSON.Patch.Prelude: and :: Foldable t => t Bool -> Bool
+ Data.JSON.Patch.Prelude: any :: Foldable t => (a -> Bool) -> t a -> Bool
+ Data.JSON.Patch.Prelude: appendFile :: FilePath -> String -> IO ()
+ Data.JSON.Patch.Prelude: asTypeOf :: a -> a -> a
+ Data.JSON.Patch.Prelude: asin :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: asinh :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: asum :: (Foldable t, Alternative f) => t (f a) -> f a
+ Data.JSON.Patch.Prelude: atan :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: atan2 :: RealFloat a => a -> a -> a
+ Data.JSON.Patch.Prelude: atanh :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d
+ Data.JSON.Patch.Prelude: break :: (a -> Bool) -> [a] -> ([a], [a])
+ Data.JSON.Patch.Prelude: ceiling :: (RealFrac a, Integral b) => a -> b
+ Data.JSON.Patch.Prelude: class a ~# b => (a :: k) ~ (b :: k)
+ Data.JSON.Patch.Prelude: class Functor f => Applicative (f :: Type -> Type)
+ Data.JSON.Patch.Prelude: class () => Bounded a
+ Data.JSON.Patch.Prelude: class () => Enum a
+ Data.JSON.Patch.Prelude: class () => Eq a
+ Data.JSON.Patch.Prelude: class (Typeable e, Show e) => Exception e
+ Data.JSON.Patch.Prelude: class Fractional a => Floating a
+ Data.JSON.Patch.Prelude: class () => Foldable (t :: Type -> Type)
+ Data.JSON.Patch.Prelude: class Num a => Fractional a
+ Data.JSON.Patch.Prelude: class () => Functor (f :: Type -> Type)
+ Data.JSON.Patch.Prelude: class () => Generic a
+ Data.JSON.Patch.Prelude: class (Real a, Enum a) => Integral a
+ Data.JSON.Patch.Prelude: class Applicative m => Monad (m :: Type -> Type)
+ Data.JSON.Patch.Prelude: class Monad m => MonadFail (m :: Type -> Type)
+ Data.JSON.Patch.Prelude: class Semigroup a => Monoid a
+ Data.JSON.Patch.Prelude: class () => Num a
+ Data.JSON.Patch.Prelude: class Eq a => Ord a
+ Data.JSON.Patch.Prelude: class () => Read a
+ Data.JSON.Patch.Prelude: class (Num a, Ord a) => Real a
+ Data.JSON.Patch.Prelude: class (RealFrac a, Floating a) => RealFloat a
+ Data.JSON.Patch.Prelude: class (Real a, Fractional a) => RealFrac a
+ Data.JSON.Patch.Prelude: class () => Semigroup a
+ Data.JSON.Patch.Prelude: class () => Show a
+ Data.JSON.Patch.Prelude: class (Functor t, Foldable t) => Traversable (t :: Type -> Type)
+ Data.JSON.Patch.Prelude: compare :: Ord a => a -> a -> Ordering
+ Data.JSON.Patch.Prelude: concat :: Foldable t => t [a] -> [a]
+ Data.JSON.Patch.Prelude: concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
+ Data.JSON.Patch.Prelude: const :: a -> b -> a
+ Data.JSON.Patch.Prelude: cos :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: cosh :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: curry :: ((a, b) -> c) -> a -> b -> c
+ Data.JSON.Patch.Prelude: cycle :: HasCallStack => [a] -> [a]
+ Data.JSON.Patch.Prelude: data () => Bool
+ Data.JSON.Patch.Prelude: data () => Char
+ Data.JSON.Patch.Prelude: data () => Double
+ Data.JSON.Patch.Prelude: data () => Either a b
+ Data.JSON.Patch.Prelude: data () => Float
+ Data.JSON.Patch.Prelude: data () => IO a
+ Data.JSON.Patch.Prelude: data () => Int
+ Data.JSON.Patch.Prelude: data () => Integer
+ Data.JSON.Patch.Prelude: data () => Maybe a
+ Data.JSON.Patch.Prelude: data () => NonEmpty a
+ Data.JSON.Patch.Prelude: data () => Ordering
+ Data.JSON.Patch.Prelude: data () => Text
+ Data.JSON.Patch.Prelude: data () => Vector a
+ Data.JSON.Patch.Prelude: data () => Word
+ Data.JSON.Patch.Prelude: decodeFloat :: RealFloat a => a -> (Integer, Int)
+ Data.JSON.Patch.Prelude: displayException :: Exception e => e -> String
+ Data.JSON.Patch.Prelude: div :: Integral a => a -> a -> a
+ Data.JSON.Patch.Prelude: divMod :: Integral a => a -> a -> (a, a)
+ Data.JSON.Patch.Prelude: drop :: Int -> [a] -> [a]
+ Data.JSON.Patch.Prelude: dropWhile :: (a -> Bool) -> [a] -> [a]
+ Data.JSON.Patch.Prelude: either :: (a -> c) -> (b -> c) -> Either a b -> c
+ Data.JSON.Patch.Prelude: elem :: (Foldable t, Eq a) => a -> t a -> Bool
+ Data.JSON.Patch.Prelude: encodeFloat :: RealFloat a => Integer -> Int -> a
+ Data.JSON.Patch.Prelude: enumFrom :: Enum a => a -> [a]
+ Data.JSON.Patch.Prelude: enumFromThen :: Enum a => a -> a -> [a]
+ Data.JSON.Patch.Prelude: enumFromThenTo :: Enum a => a -> a -> a -> [a]
+ Data.JSON.Patch.Prelude: enumFromTo :: Enum a => a -> a -> [a]
+ Data.JSON.Patch.Prelude: error :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => [Char] -> a
+ Data.JSON.Patch.Prelude: errorWithoutStackTrace :: forall (r :: RuntimeRep) (a :: TYPE r). [Char] -> a
+ Data.JSON.Patch.Prelude: even :: Integral a => a -> Bool
+ Data.JSON.Patch.Prelude: exp :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: exponent :: RealFloat a => a -> Int
+ Data.JSON.Patch.Prelude: fail :: MonadFail m => String -> m a
+ Data.JSON.Patch.Prelude: filter :: (a -> Bool) -> [a] -> [a]
+ Data.JSON.Patch.Prelude: first :: Bifunctor p => (a -> b) -> p a c -> p b c
+ Data.JSON.Patch.Prelude: flip :: (a -> b -> c) -> b -> a -> c
+ Data.JSON.Patch.Prelude: floatDigits :: RealFloat a => a -> Int
+ Data.JSON.Patch.Prelude: floatRadix :: RealFloat a => a -> Integer
+ Data.JSON.Patch.Prelude: floatRange :: RealFloat a => a -> (Int, Int)
+ Data.JSON.Patch.Prelude: floor :: (RealFrac a, Integral b) => a -> b
+ Data.JSON.Patch.Prelude: fmap :: Functor f => (a -> b) -> f a -> f b
+ Data.JSON.Patch.Prelude: foldM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
+ Data.JSON.Patch.Prelude: foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
+ Data.JSON.Patch.Prelude: foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
+ Data.JSON.Patch.Prelude: foldl1 :: Foldable t => (a -> a -> a) -> t a -> a
+ Data.JSON.Patch.Prelude: foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
+ Data.JSON.Patch.Prelude: foldr1 :: Foldable t => (a -> a -> a) -> t a -> a
+ Data.JSON.Patch.Prelude: fromEnum :: Enum a => a -> Int
+ Data.JSON.Patch.Prelude: fromException :: Exception e => SomeException -> Maybe e
+ Data.JSON.Patch.Prelude: fromInteger :: Num a => Integer -> a
+ Data.JSON.Patch.Prelude: fromIntegral :: (Integral a, Num b) => a -> b
+ Data.JSON.Patch.Prelude: fromMaybe :: a -> Maybe a -> a
+ Data.JSON.Patch.Prelude: fromRational :: Fractional a => Rational -> a
+ Data.JSON.Patch.Prelude: fst :: (a, b) -> a
+ Data.JSON.Patch.Prelude: gcd :: Integral a => a -> a -> a
+ Data.JSON.Patch.Prelude: getChar :: IO Char
+ Data.JSON.Patch.Prelude: getContents :: IO String
+ Data.JSON.Patch.Prelude: getLine :: IO String
+ Data.JSON.Patch.Prelude: guard :: Alternative f => Bool -> f ()
+ Data.JSON.Patch.Prelude: head :: HasCallStack => [a] -> a
+ Data.JSON.Patch.Prelude: id :: a -> a
+ Data.JSON.Patch.Prelude: infix 4 `notElem`
+ Data.JSON.Patch.Prelude: infixl 1 >>
+ Data.JSON.Patch.Prelude: infixl 3 <|>
+ Data.JSON.Patch.Prelude: infixl 4 <$>
+ Data.JSON.Patch.Prelude: infixl 6 +
+ Data.JSON.Patch.Prelude: infixl 7 *
+ Data.JSON.Patch.Prelude: infixl 9 !!
+ Data.JSON.Patch.Prelude: infixr 0 $!
+ Data.JSON.Patch.Prelude: infixr 1 =<<
+ Data.JSON.Patch.Prelude: infixr 2 ||
+ Data.JSON.Patch.Prelude: infixr 3 &&
+ Data.JSON.Patch.Prelude: infixr 5 ++
+ Data.JSON.Patch.Prelude: infixr 6 <>
+ Data.JSON.Patch.Prelude: infixr 8 ^^
+ Data.JSON.Patch.Prelude: infixr 9 .
+ Data.JSON.Patch.Prelude: init :: HasCallStack => [a] -> [a]
+ Data.JSON.Patch.Prelude: interact :: (String -> String) -> IO ()
+ Data.JSON.Patch.Prelude: ioError :: IOError -> IO a
+ Data.JSON.Patch.Prelude: isDenormalized :: RealFloat a => a -> Bool
+ Data.JSON.Patch.Prelude: isIEEE :: RealFloat a => a -> Bool
+ Data.JSON.Patch.Prelude: isInfinite :: RealFloat a => a -> Bool
+ Data.JSON.Patch.Prelude: isNaN :: RealFloat a => a -> Bool
+ Data.JSON.Patch.Prelude: isNegativeZero :: RealFloat a => a -> Bool
+ Data.JSON.Patch.Prelude: iterate :: (a -> a) -> a -> [a]
+ Data.JSON.Patch.Prelude: last :: HasCallStack => [a] -> a
+ Data.JSON.Patch.Prelude: lcm :: Integral a => a -> a -> a
+ Data.JSON.Patch.Prelude: length :: Foldable t => t a -> Int
+ Data.JSON.Patch.Prelude: lex :: ReadS String
+ Data.JSON.Patch.Prelude: liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
+ Data.JSON.Patch.Prelude: lines :: String -> [String]
+ Data.JSON.Patch.Prelude: log :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: logBase :: Floating a => a -> a -> a
+ Data.JSON.Patch.Prelude: lookup :: Eq a => a -> [(a, b)] -> Maybe b
+ Data.JSON.Patch.Prelude: map :: (a -> b) -> [a] -> [b]
+ Data.JSON.Patch.Prelude: mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
+ Data.JSON.Patch.Prelude: mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()
+ Data.JSON.Patch.Prelude: mappend :: Monoid a => a -> a -> a
+ Data.JSON.Patch.Prelude: max :: Ord a => a -> a -> a
+ Data.JSON.Patch.Prelude: maxBound :: Bounded a => a
+ Data.JSON.Patch.Prelude: maximum :: (Foldable t, Ord a) => t a -> a
+ Data.JSON.Patch.Prelude: maybe :: b -> (a -> b) -> Maybe a -> b
+ Data.JSON.Patch.Prelude: mconcat :: Monoid a => [a] -> a
+ Data.JSON.Patch.Prelude: mempty :: Monoid a => a
+ Data.JSON.Patch.Prelude: min :: Ord a => a -> a -> a
+ Data.JSON.Patch.Prelude: minBound :: Bounded a => a
+ Data.JSON.Patch.Prelude: minimum :: (Foldable t, Ord a) => t a -> a
+ Data.JSON.Patch.Prelude: mod :: Integral a => a -> a -> a
+ Data.JSON.Patch.Prelude: negate :: Num a => a -> a
+ Data.JSON.Patch.Prelude: nonEmpty :: [a] -> Maybe (NonEmpty a)
+ Data.JSON.Patch.Prelude: not :: Bool -> Bool
+ Data.JSON.Patch.Prelude: notElem :: (Foldable t, Eq a) => a -> t a -> Bool
+ Data.JSON.Patch.Prelude: note :: e -> Maybe a -> Either e a
+ Data.JSON.Patch.Prelude: null :: Foldable t => t a -> Bool
+ Data.JSON.Patch.Prelude: odd :: Integral a => a -> Bool
+ Data.JSON.Patch.Prelude: or :: Foldable t => t Bool -> Bool
+ Data.JSON.Patch.Prelude: otherwise :: Bool
+ Data.JSON.Patch.Prelude: pack :: String -> Text
+ Data.JSON.Patch.Prelude: pi :: Floating a => a
+ Data.JSON.Patch.Prelude: pred :: Enum a => a -> a
+ Data.JSON.Patch.Prelude: print :: Show a => a -> IO ()
+ Data.JSON.Patch.Prelude: product :: (Foldable t, Num a) => t a -> a
+ Data.JSON.Patch.Prelude: properFraction :: (RealFrac a, Integral b) => a -> (b, a)
+ Data.JSON.Patch.Prelude: pure :: Applicative f => a -> f a
+ Data.JSON.Patch.Prelude: putChar :: Char -> IO ()
+ Data.JSON.Patch.Prelude: putStr :: String -> IO ()
+ Data.JSON.Patch.Prelude: putStrLn :: String -> IO ()
+ Data.JSON.Patch.Prelude: quot :: Integral a => a -> a -> a
+ Data.JSON.Patch.Prelude: quotRem :: Integral a => a -> a -> (a, a)
+ Data.JSON.Patch.Prelude: read :: Read a => String -> a
+ Data.JSON.Patch.Prelude: readFile :: FilePath -> IO String
+ Data.JSON.Patch.Prelude: readIO :: Read a => String -> IO a
+ Data.JSON.Patch.Prelude: readList :: Read a => ReadS [a]
+ Data.JSON.Patch.Prelude: readLn :: Read a => IO a
+ Data.JSON.Patch.Prelude: readParen :: Bool -> ReadS a -> ReadS a
+ Data.JSON.Patch.Prelude: reads :: Read a => ReadS a
+ Data.JSON.Patch.Prelude: readsPrec :: Read a => Int -> ReadS a
+ Data.JSON.Patch.Prelude: realToFrac :: (Real a, Fractional b) => a -> b
+ Data.JSON.Patch.Prelude: recip :: Fractional a => a -> a
+ Data.JSON.Patch.Prelude: rem :: Integral a => a -> a -> a
+ Data.JSON.Patch.Prelude: repeat :: a -> [a]
+ Data.JSON.Patch.Prelude: replicate :: Int -> a -> [a]
+ Data.JSON.Patch.Prelude: return :: Monad m => a -> m a
+ Data.JSON.Patch.Prelude: reverse :: [a] -> [a]
+ Data.JSON.Patch.Prelude: round :: (RealFrac a, Integral b) => a -> b
+ Data.JSON.Patch.Prelude: scaleFloat :: RealFloat a => Int -> a -> a
+ Data.JSON.Patch.Prelude: scanl :: (b -> a -> b) -> b -> [a] -> [b]
+ Data.JSON.Patch.Prelude: scanl1 :: (a -> a -> a) -> [a] -> [a]
+ Data.JSON.Patch.Prelude: scanr :: (a -> b -> b) -> b -> [a] -> [b]
+ Data.JSON.Patch.Prelude: scanr1 :: (a -> a -> a) -> [a] -> [a]
+ Data.JSON.Patch.Prelude: second :: Bifunctor p => (b -> c) -> p a b -> p a c
+ Data.JSON.Patch.Prelude: seq :: forall {r :: RuntimeRep} a (b :: TYPE r). a -> b -> b
+ Data.JSON.Patch.Prelude: sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
+ Data.JSON.Patch.Prelude: sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
+ Data.JSON.Patch.Prelude: sequence_ :: (Foldable t, Monad m) => t (m a) -> m ()
+ Data.JSON.Patch.Prelude: show :: Show a => a -> String
+ Data.JSON.Patch.Prelude: showChar :: Char -> ShowS
+ Data.JSON.Patch.Prelude: showList :: Show a => [a] -> ShowS
+ Data.JSON.Patch.Prelude: showParen :: Bool -> ShowS -> ShowS
+ Data.JSON.Patch.Prelude: showString :: String -> ShowS
+ Data.JSON.Patch.Prelude: shows :: Show a => a -> ShowS
+ Data.JSON.Patch.Prelude: showsPrec :: Show a => Int -> a -> ShowS
+ Data.JSON.Patch.Prelude: significand :: RealFloat a => a -> a
+ Data.JSON.Patch.Prelude: signum :: Num a => a -> a
+ Data.JSON.Patch.Prelude: sin :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: sinh :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: snd :: (a, b) -> b
+ Data.JSON.Patch.Prelude: span :: (a -> Bool) -> [a] -> ([a], [a])
+ Data.JSON.Patch.Prelude: splitAt :: Int -> [a] -> ([a], [a])
+ Data.JSON.Patch.Prelude: sqrt :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: subtract :: Num a => a -> a -> a
+ Data.JSON.Patch.Prelude: succ :: Enum a => a -> a
+ Data.JSON.Patch.Prelude: sum :: (Foldable t, Num a) => t a -> a
+ Data.JSON.Patch.Prelude: tail :: HasCallStack => [a] -> [a]
+ Data.JSON.Patch.Prelude: take :: Int -> [a] -> [a]
+ Data.JSON.Patch.Prelude: takeWhile :: (a -> Bool) -> [a] -> [a]
+ Data.JSON.Patch.Prelude: tan :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: tanh :: Floating a => a -> a
+ Data.JSON.Patch.Prelude: toEnum :: Enum a => Int -> a
+ Data.JSON.Patch.Prelude: toException :: Exception e => e -> SomeException
+ Data.JSON.Patch.Prelude: toInteger :: Integral a => a -> Integer
+ Data.JSON.Patch.Prelude: toRational :: Real a => a -> Rational
+ Data.JSON.Patch.Prelude: traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
+ Data.JSON.Patch.Prelude: truncate :: (RealFrac a, Integral b) => a -> b
+ Data.JSON.Patch.Prelude: type FilePath = String
+ Data.JSON.Patch.Prelude: type IOError = IOException
+ Data.JSON.Patch.Prelude: type Rational = Ratio Integer
+ Data.JSON.Patch.Prelude: type ReadS a = String -> [(a, String)]
+ Data.JSON.Patch.Prelude: type ShowS = String -> String
+ Data.JSON.Patch.Prelude: type String = [Char]
+ Data.JSON.Patch.Prelude: uncurry :: (a -> b -> c) -> (a, b) -> c
+ Data.JSON.Patch.Prelude: undefined :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => a
+ Data.JSON.Patch.Prelude: unless :: Applicative f => Bool -> f () -> f ()
+ Data.JSON.Patch.Prelude: unlines :: [String] -> String
+ Data.JSON.Patch.Prelude: unpack :: Text -> String
+ Data.JSON.Patch.Prelude: until :: (a -> Bool) -> (a -> a) -> a -> a
+ Data.JSON.Patch.Prelude: unwords :: [String] -> String
+ Data.JSON.Patch.Prelude: unzip :: [(a, b)] -> ([a], [b])
+ Data.JSON.Patch.Prelude: unzip3 :: [(a, b, c)] -> ([a], [b], [c])
+ Data.JSON.Patch.Prelude: userError :: String -> IOError
+ Data.JSON.Patch.Prelude: void :: Functor f => f a -> f ()
+ Data.JSON.Patch.Prelude: when :: Applicative f => Bool -> f () -> f ()
+ Data.JSON.Patch.Prelude: words :: String -> [String]
+ Data.JSON.Patch.Prelude: writeFile :: FilePath -> String -> IO ()
+ Data.JSON.Patch.Prelude: zip :: [a] -> [b] -> [(a, b)]
+ Data.JSON.Patch.Prelude: zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
+ Data.JSON.Patch.Prelude: zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
+ Data.JSON.Patch.Prelude: zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
+ Data.JSON.Pointer: Pointer :: [Token] -> Pointer
+ Data.JSON.Pointer: [$sel:tokens:Pointer] :: Pointer -> [Token]
+ Data.JSON.Pointer: atPointerL :: Pointer -> AffineTraversal' Value (Maybe Value)
+ Data.JSON.Pointer: newtype Pointer
+ Data.JSON.Pointer: pointerL :: Pointer -> AffineTraversal' Value Value
+ Data.JSON.Pointer: splitPointer :: Pointer -> Maybe (Pointer, Token)
+ Data.JSON.Pointer.Token: E :: Token
+ Data.JSON.Pointer.Token: tokenFromText :: Text -> Either String Token
+ Data.JSON.Pointer.Token: tokenToText :: Token -> Text
- Data.JSON.Patch.Apply: EmptyArray :: [Token] -> PatchError
+ Data.JSON.Patch.Apply: EmptyArray :: Pointer -> PatchError
- Data.JSON.Patch.Apply: IndexOutOfBounds :: [Token] -> Int -> Vector Value -> PatchError
+ Data.JSON.Patch.Apply: IndexOutOfBounds :: Pointer -> Int -> Vector Value -> PatchError
- Data.JSON.Patch.Apply: InvalidArrayOperation :: [Token] -> Value -> PatchError
+ Data.JSON.Patch.Apply: InvalidArrayOperation :: Pointer -> Value -> PatchError
- Data.JSON.Patch.Apply: InvalidObjectOperation :: [Token] -> Value -> PatchError
+ Data.JSON.Patch.Apply: InvalidObjectOperation :: Pointer -> Value -> PatchError
- Data.JSON.Patch.Apply: PointerNotFound :: [Token] -> Maybe String -> PatchError
+ Data.JSON.Patch.Apply: PointerNotFound :: Pointer -> Maybe String -> PatchError
- Data.JSON.Patch.Error: EmptyArray :: [Token] -> PatchError
+ Data.JSON.Patch.Error: EmptyArray :: Pointer -> PatchError
- Data.JSON.Patch.Error: IndexOutOfBounds :: [Token] -> Int -> Vector Value -> PatchError
+ Data.JSON.Patch.Error: IndexOutOfBounds :: Pointer -> Int -> Vector Value -> PatchError
- Data.JSON.Patch.Error: InvalidArrayOperation :: [Token] -> Value -> PatchError
+ Data.JSON.Patch.Error: InvalidArrayOperation :: Pointer -> Value -> PatchError
- Data.JSON.Patch.Error: InvalidObjectOperation :: [Token] -> Value -> PatchError
+ Data.JSON.Patch.Error: InvalidObjectOperation :: Pointer -> Value -> PatchError
- Data.JSON.Patch.Error: PointerNotFound :: [Token] -> Maybe String -> PatchError
+ Data.JSON.Patch.Error: PointerNotFound :: Pointer -> Maybe String -> PatchError

Files

jsonpatch.cabal view
@@ -1,6 +1,6 @@ cabal-version:      1.18 name:               jsonpatch-version:            0.1.0.0+version:            0.2.0.0 license:            AGPL-3 license-file:       COPYING maintainer:         Patrick Brisbin@@ -39,6 +39,7 @@         Data.JSON.Patch         Data.JSON.Patch.Apply         Data.JSON.Patch.Error+        Data.JSON.Patch.Prelude         Data.JSON.Patch.Type         Data.JSON.Pointer         Data.JSON.Pointer.Token@@ -64,8 +65,7 @@         aeson-optics >=1.2.0.1,         attoparsec >=0.14.4,         base >=4.16.4.0 && <5,-        mtl >=2.2.2,-        optics >=0.4.2,+        optics-core >=0.4.1,         text >=1.2.5.0,         vector >=0.12.3.1 @@ -132,6 +132,7 @@         Data.Aeson.Optics.ExtSpec         Data.JSON.Patch.PointerSpec         Data.JSON.PatchSpec+        Data.JSON.Pointer.TokenSpec         Data.Vector.ExtSpec         Paths_jsonpatch @@ -159,7 +160,7 @@         hspec >=2.9.7,         hspec-expectations-json >=1.0.0.7,         jsonpatch,-        optics >=0.4.2,+        optics-core >=0.4.1,         path >=0.9.2,         vector >=0.12.3.1 
src/Data/Aeson/Optics/Ext.hs view
@@ -9,6 +9,7 @@ module Data.Aeson.Optics.Ext   ( atKey   , atNth+  , atEnd   ) where  import Prelude@@ -19,7 +20,7 @@ import Data.Aeson.Optics import Data.Vector qualified as V import Data.Vector.Ext qualified as V-import Optics+import Optics.Core  -- | Like 'key', but uses 'at' instead of 'ix'. This is handy when adding and -- removing object keys:@@ -68,4 +69,32 @@     Just x -> case nv of       Object km -> Object $ KeyMap.insert (Key.fromString $ show n) x km       Array vec -> Array $ V.insertAt n x vec+      v -> v++-- | List 'atNth' for the index /after/ the last value of an 'Array'+--+-- This only useful to normalize adds as a lens:+--+-- >>> ['a', 'b', 'c'] & atEnd ?~ 'x'+-- ['a', 'b', 'c', 'x']+--+-- Attempting to access this index on an array will always given 'Nothing'+--+-- >>> ['a', 'b', 'c'] ^? atEnd+-- Nothing+--+-- Most other operations are going to be a no-op.+atEnd :: AffineTraversal' Value (Maybe Value)+atEnd = atraversal matcher updater+ where+  matcher :: Value -> Either Value (Maybe Value)+  matcher = \case+    Array {} -> Right Nothing -- index after end doesn't exist+    v -> Left v++  updater :: Value -> Maybe Value -> Value+  updater nv = \case+    Nothing -> nv+    Just x -> case nv of+      Array vec -> Array $ vec <> pure x       v -> v
src/Data/JSON/Patch/Apply.hs view
@@ -11,111 +11,56 @@   , PatchError (..)   ) where -import Prelude+import Data.JSON.Patch.Prelude -import Control.Monad (unless, void, when)-import Control.Monad.Except (MonadError, runExcept, throwError)-import Control.Monad.State (MonadState, execStateT, gets, modify, put)-import Data.Aeson-import Data.Aeson.KeyMap (KeyMap)-import Data.Aeson.Optics-import Data.Foldable (traverse_)+import Data.Aeson (Value (..)) import Data.JSON.Patch.Error import Data.JSON.Patch.Type import Data.JSON.Pointer import Data.JSON.Pointer.Token-import Data.Vector (Vector) import Data.Vector qualified as V-import Optics+import Optics.Core  applyPatches :: [Patch] -> Value -> Either PatchError Value-applyPatches ps = runExcept . execStateT (traverse_ applyPatch ps)--applyPatch :: (MonadError PatchError m, MonadState Value m) => Patch -> m ()-applyPatch = \case-  Add op -> add op.value op.path-  Remove op -> remove op.path-  Replace op -> remove op.path >> add op.value op.path-  Move op -> do-    v <- get op.from-    remove op.from-    add v op.path-  Copy op -> flip add op.path =<< get op.from-  Test op -> do-    v <- get op.path-    unless (v == op.value) $ throwError $ TestFailed op.path v op.value--get :: (MonadError PatchError m, MonadState Value m) => Pointer -> m Value-get = \case-  PointerEmpty -> gets id-  PointerPath ts t -> assertExists $ ts <> [t]-  PointerPathEnd ts -> do-    target <- assertExists ts-    vec <- assertArray ts target-    snd <$> assertUnsnoc ts vec--add :: (MonadError PatchError m, MonadState Value m) => Value -> Pointer -> m ()-add v = \case-  PointerEmpty -> put v-  PointerPath ts t -> do-    target <- assertExists ts--    -- Additional validations based on type of final target-    case t of-      K _ -> void $ assertObject ts target-      N n -> do-        case target of-          Object {} -> pure () -- n will be used as Key, no bounds check-          Array vec -> do-            when (n < 0) $ throwError $ IndexOutOfBounds ts n vec-            when (n > V.length vec) $ throwError $ IndexOutOfBounds ts n vec-          v' -> throwError $ InvalidArrayOperation ts v'--    modify $ tokensL ts % atTokenL t ?~ v-  PointerPathEnd ts -> do-    target <- assertExists ts-    void $ assertArray ts target-    modify $ tokensL ts % _Array %~ (<> pure v)+applyPatches ps v = foldM applyPatch v ps -remove :: (MonadError PatchError m, MonadState Value m) => Pointer -> m ()-remove = \case-  PointerEmpty -> put Null -- NB. unspecified behavior-  PointerPath ts t -> do-    void $ assertExists $ ts <> [t]+applyPatch :: Value -> Patch -> Either PatchError Value+applyPatch val = \case+  Add op -> add op.value op.path val+  Remove op -> remove op.path val+  Replace op -> remove op.path val >>= add op.value op.path+  Move op -> get op.from val >>= \v -> remove op.from val >>= add v op.path+  Copy op -> get op.from val >>= \v -> add v op.path val+  Test op -> get op.path val >>= \v -> test v op.value op.path val -    -- NB. odd that the tests don't exercise any additional validation (e.g.-    -- bounds checking) like we saw with add.+get :: Pointer -> Value -> Either PatchError Value+get p val = note (PointerNotFound p Nothing) $ val ^? pointerL p -    modify $ tokensL ts % atTokenL t .~ Nothing-  PointerPathEnd ts -> do-    target <- assertExists ts-    vec <- assertArray ts target-    (vs, _) <- assertUnsnoc ts vec-    modify $ tokensL ts % _Array .~ vs+add :: Value -> Pointer -> Value -> Either PatchError Value+add v p val = case splitPointer p of+  Nothing -> Right v+  Just (parent, t) -> do+    validateAdd parent t val+    Right $ val & atPointerL p ?~ v -assertExists-  :: (MonadError PatchError m, MonadState Value m) => [Token] -> m Value-assertExists ts =-  gets (preview $ tokensL ts) >>= \case-    Nothing -> throwError $ PointerNotFound ts Nothing-    Just v -> pure v+remove :: Pointer -> Value -> Either PatchError Value+remove p val = do+  void $ get p val+  Right $ val & atPointerL p .~ Nothing -assertObject :: MonadError PatchError m => [Token] -> Value -> m (KeyMap Value)-assertObject ts = \case-  Object o -> pure o-  v -> throwError $ InvalidObjectOperation ts v+test :: Value -> Value -> Pointer -> Value -> Either PatchError Value+test v expected p val =+  note (TestFailed p v expected) $ val <$ guard (v == expected) -assertArray :: MonadError PatchError m => [Token] -> Value -> m (Vector Value)-assertArray ts = \case-  Array vec -> pure vec-  v -> throwError $ InvalidArrayOperation ts v+validateAdd :: Pointer -> Token -> Value -> Either PatchError ()+validateAdd parent t val = do+  target <- get parent val -assertUnsnoc-  :: MonadError PatchError m-  => [Token]-  -> Vector Value-  -> m (Vector Value, Value)-assertUnsnoc ts vec =-  case V.unsnoc vec of-    Nothing -> throwError $ EmptyArray ts-    Just tp -> pure tp+  case (t, target) of+    (_, Object _) -> Right () -- everything works on objects+    (K _, v) -> Left $ InvalidObjectOperation parent v+    (N n, Array vec) -> do+      when (n < 0) $ Left $ IndexOutOfBounds parent n vec+      when (n > V.length vec) $ Left $ IndexOutOfBounds parent n vec+    (N _, v) -> Left $ InvalidArrayOperation parent v+    _ -> Right ()
src/Data/JSON/Patch/Error.hs view
@@ -10,22 +10,19 @@   ( PatchError (..)   ) where -import Prelude+import Data.JSON.Patch.Prelude -import Control.Exception (Exception (..)) import Data.Aeson (Value) import Data.JSON.Pointer-import Data.JSON.Pointer.Token-import Data.Vector (Vector) import Data.Vector qualified as V  data PatchError   = ParseError Value String-  | PointerNotFound [Token] (Maybe String)-  | InvalidObjectOperation [Token] Value-  | InvalidArrayOperation [Token] Value-  | IndexOutOfBounds [Token] Int (Vector Value)-  | EmptyArray [Token]+  | PointerNotFound Pointer (Maybe String)+  | InvalidObjectOperation Pointer Value+  | InvalidArrayOperation Pointer Value+  | IndexOutOfBounds Pointer Int (Vector Value)+  | EmptyArray Pointer   | TestFailed Pointer Value Value   deriving stock (Show) @@ -37,17 +34,17 @@         <> ("\n  input: " <> show v)     PointerNotFound ts mType ->       "Path "-        <> tokensToString ts+        <> pointerToString ts         <> " does not exist"         <> maybe "" (" or is not " <>) mType     InvalidObjectOperation ts v ->       "Cannot perform object operation on non-object at "-        <> tokensToString ts+        <> pointerToString ts         <> ": "         <> show v     InvalidArrayOperation ts v ->       "Cannot perform array operation on non-array at "-        <> tokensToString ts+        <> pointerToString ts         <> ": "         <> show v     IndexOutOfBounds ts n vec ->@@ -56,9 +53,9 @@         <> " is out of bounds for vector of length "         <> show (V.length vec)         <> " at "-        <> tokensToString ts+        <> pointerToString ts     EmptyArray ts ->-      "Cannot perform operation on empty array at " <> tokensToString ts+      "Cannot perform operation on empty array at " <> pointerToString ts     TestFailed p actual expected ->       "Test failed at "         <> pointerToString p
+ src/Data/JSON/Patch/Prelude.hs view
@@ -0,0 +1,19 @@+module Data.JSON.Patch.Prelude+  ( module X+  , note+  ) where++import Prelude as X++import Control.Applicative as X (asum, (<|>))+import Control.Exception as X (Exception (..))+import Control.Monad as X (foldM, guard, unless, void, when)+import Data.Bifunctor as X (bimap, first, second)+import Data.List.NonEmpty as X (NonEmpty, nonEmpty)+import Data.Maybe as X (fromMaybe)+import Data.Text as X (Text, pack, unpack)+import Data.Vector as X (Vector)+import GHC.Generics as X (Generic)++note :: e -> Maybe a -> Either e a+note e = maybe (Left e) Right
src/Data/JSON/Patch/Type.hs view
@@ -16,12 +16,10 @@   , TestOp (..)   ) where -import Prelude+import Data.JSON.Patch.Prelude  import Data.Aeson import Data.JSON.Pointer-import Data.Text (Text)-import GHC.Generics (Generic)  data Patch   = Add AddOp
src/Data/JSON/Pointer.hs view
@@ -4,57 +4,60 @@   , pointerFromText   , pointerToText   , pointerToString+  , pointerL+  , atPointerL+  , splitPointer   ) where -import Prelude+import Data.JSON.Patch.Prelude -import Data.Aeson (FromJSON (..), withText)+import Data.Aeson (FromJSON (..), Value, withText)+import Data.Aeson.Optics (_JSON) import Data.Attoparsec.Text import Data.JSON.Pointer.Token-import Data.List.NonEmpty (nonEmpty) import Data.List.NonEmpty qualified as NE-import Data.Text (Text, unpack) import Data.Text qualified as T+import Optics.Core -data Pointer-  = -- | @""@ means whole-document-    PointerEmpty-  | -- | @"/[.../]x"@ means path to a key or index @x@-    ---    -- NB. @"/"@ naturally becomes @'PointerPath' [] ('K' "")@-    PointerPath [Token] Token-  | -- | @"/[.../]-"@ means path to last element of an array-    PointerPathEnd [Token]+newtype Pointer = Pointer+  { tokens :: [Token]+  }   deriving stock (Eq, Show)  instance FromJSON Pointer where   parseJSON = withText "Pointer" $ either fail pure . pointerFromText  pointerFromText :: Text -> Either String Pointer-pointerFromText = \case-  "" -> Right PointerEmpty-  "/" -> Right $ PointerPath [] $ K ""-  "/-" -> Right $ PointerPathEnd []-  p -> case T.stripSuffix "/-" p of-    Nothing -> parseOnly pointerPathP p-    Just p' -> parseOnly pointerPathEndP p'+pointerFromText = parseOnly pointerP +pointerP :: Parser Pointer+pointerP = do+  ts <- (char '/' *> tokenP `sepBy1` char '/' <|> pure []) <* endOfInput+  pure $ Pointer ts++tokenP :: Parser Token+tokenP =+  either (fail . ("invalid token: " <>)) pure+    . tokenFromText+    =<< takeTill (== '/')+ pointerToText :: Pointer -> Text-pointerToText = \case-  PointerEmpty -> ""-  PointerPath ts t -> tokensToText $ ts <> [t]-  PointerPathEnd ts -> tokensToText ts <> "/-"+pointerToText = ("/" <>) . T.intercalate "/" . map tokenToText . (.tokens)  pointerToString :: Pointer -> String pointerToString = unpack . pointerToText -pointerPathP :: Parser Pointer-pointerPathP = do-  ts <- maybe (fail "") pure . nonEmpty =<< pointerP-  pure $ PointerPath (NE.init ts) $ NE.last ts+-- | Access a 'Pointer' like 'ix', used for indexing+pointerL :: Pointer -> AffineTraversal' Value Value+pointerL = foldr ((%) . tokenL) (castOptic simple) . (.tokens) -pointerPathEndP :: Parser Pointer-pointerPathEndP = PointerPathEnd <$> pointerP+-- | Access a 'Pointer' like 'at', used for adding or removing+atPointerL :: Pointer -> AffineTraversal' Value (Maybe Value)+atPointerL p = case splitPointer p of+  Nothing -> castOptic _JSON -- hack to return as-is+  Just (parent, t) -> pointerL parent % atTokenL t -pointerP :: Parser [Token]-pointerP = char '/' *> tokenP `sepBy1` char '/' <* endOfInput+splitPointer :: Pointer -> Maybe (Pointer, Token)+splitPointer p = go <$> nonEmpty p.tokens+ where+  go x = (Pointer $ NE.init x, NE.last x)
src/Data/JSON/Pointer/Token.hs view
@@ -8,83 +8,61 @@ -- Portability : POSIX module Data.JSON.Pointer.Token   ( Token (..)-  , tokenP-  , tokensToText-  , tokensToString-  , tokensL+  , tokenFromText+  , tokenToText   , tokenL   , atTokenL   ) where -import Prelude+import Data.JSON.Patch.Prelude -import Control.Applicative (optional, (<|>)) import Data.Aeson (Key, Value (..)) import Data.Aeson.Key qualified as Key-import Data.Aeson.Optics+import Data.Aeson.Optics (key, nth) import Data.Aeson.Optics.Ext-import Data.Attoparsec.Text-import Data.Text (Text, pack, unpack)+import Data.Char (isDigit) import Data.Text qualified as T-import Optics+import Optics.Core import Text.Read (readEither) -data Token = K Key | N Int+data Token = N Int | E | K Key   deriving stock (Eq, Show) -tokensToString :: [Token] -> String-tokensToString = unpack . tokensToText--tokensToText :: [Token] -> Text-tokensToText ts = "/" <> T.intercalate "/" (map tokenToText ts)--tokenToText :: Token -> Text-tokenToText = \case-  K k -> Key.toText k-  N n -> pack $ show n--tokensL :: [Token] -> AffineTraversal' Value Value-tokensL = foldr ((%) . tokenL) $ castOptic simple---- | Access a key or array index like 'ix'+-- | Access a key or array index like 'ix', used for indexing tokenL :: Token -> AffineTraversal' Value Value tokenL t = case t of-  K k -> key k   N n -> nth n+  E -> atEnd % _Just+  K k -> key k --- | Access a key or array index, but 'at'-like+-- | Access a key or array index like 'at', used for adding or removing atTokenL :: Token -> AffineTraversal' Value (Maybe Value) atTokenL = \case-  K k -> atKey k   N n -> atNth n--tokenP :: Parser Token-tokenP = wtf <|> N <$> indexP <|> K <$> keyP---- |------ The tests love to use this value as an example. I'm clearly confused because--- I would expect it to fail 'indexP', backtrack, then succeed in 'keyP', but it--- causes errors with "endOfInput", so we'll special case it for now.-wtf :: Parser Token-wtf = K . Key.fromText <$> string "1e0"+  E -> atEnd+  K k -> atKey k -keyP :: Parser Key-keyP =-  Key.fromText-    . T.replace "~0" "~"-    . T.replace "~1" "/"-    <$> takeTill (== '/')+tokenFromText :: Text -> Either String Token+tokenFromText = \case+  "" -> Right $ K ""+  "-" -> Right E+  t | T.all isDigit t -> N <$> readDigits t+  -- The spec doesn't allow this (negative indexes), but the tests use it to+  -- trigger the lower bounds error example.+  t | Just ('-', n) <- T.uncons t, T.all isDigit n -> N . negate <$> readDigits n+  t -> Right $ K $ Key.fromText $ T.replace "~0" "~" $ T.replace "~1" "/" t -indexP :: Parser Int-indexP = do-  f <- maybe id (const negate) <$> optional (char '-')-  f <$> nonzeroP <|> 0 <$ char '0'+readDigits :: Text -> Either String Int+readDigits t+  | t == "0" = Right 0+  | T.isPrefixOf "0" t = Left "leading zeros"+  | otherwise =+      first (\msg -> "could not read digits " <> unpack t <> ": " <> msg)+        $ readEither+        $ unpack t -nonzeroP :: Parser Int-nonzeroP = do-  ds <- (:) <$> satisfy (inClass "1-9") <*> many' digit-  either (err ds) pure $ readEither ds- where-  err :: String -> String -> Parser a-  err x msg = fail $ "Unable to read integer from " <> x <> ": " <> msg+tokenToText :: Token -> Text+tokenToText = \case+  K k -> T.replace "/" "~1" $ T.replace "~" "~0" $ Key.toText k+  N n -> pack $ show n+  E -> "-"
test/Data/Aeson/Optics/ExtSpec.hs view
@@ -17,7 +17,7 @@ import Data.Aeson.Optics.Ext import Data.Aeson.QQ import Data.Foldable (for_)-import Optics+import Optics.Core import Test.Hspec  spec :: Spec@@ -49,7 +49,7 @@       input & atNth 2 .~ Nothing & (`shouldBe` [aesonQQ| ["a", "b"] |])       input & atNth 3 .~ Nothing & (`shouldBe` input) -    context "targeting an Object" $ do+    context "Objects behave like atKey" $ do       let input = [aesonQQ| {"0":"a", "1":"b", "2":"c"} |]        for_ [-1 .. 3] $ \n -> do@@ -59,11 +59,28 @@           add l = input & l ?~ "x"           remove l = input & l .~ Nothing -        it ("indexes like atKey for " <> show n) $ do+        it ("test index " <> show n) $ do           index (atNth n) `shouldBe` index (atKey k)--        it ("adds like atKey for " <> show n) $ do           add (atNth n) `shouldBe` add (atKey k)--        it ("removes like atKey for " <> show n) $ do           remove (atNth n) `shouldBe` remove (atKey k)++  describe "atEnd" $ do+    it "always indexes as Nothing" $ do+      let input = [aesonQQ| ["a", "b"] |]++      input ^? atEnd % _Just `shouldBe` Nothing++    it "allows appending to an array" $ do+      let input = [aesonQQ| ["a", "b"] |]++      input & atEnd ?~ "x" & (`shouldBe` [aesonQQ| ["a", "b", "x"] |])++    it "cannot remove the non-existent index" $ do+      let input = [aesonQQ| ["a", "b"] |]++      input & atEnd .~ Nothing & (`shouldBe` input)++    it "leaves other types alone" $ do+      let input = [aesonQQ| {"a": "b"} |]++      input & atEnd ?~ "x" & (`shouldBe` input)
test/Data/JSON/Patch/PointerSpec.hs view
@@ -21,33 +21,34 @@   describe "pointerFromText" $ do     context "success" $ do       it "empty" $ do-        pointerFromText "" `shouldBe` Right PointerEmpty+        pointerFromText "" `shouldBe` Right (Pointer [])        it "root-only" $ do-        pointerFromText "/" `shouldBe` Right (PointerPath [] $ K "")+        pointerFromText "/" `shouldBe` Right (Pointer [K ""])        it "path" $ do         pointerFromText "/foo/0/bar/1/baz"           `shouldBe` Right-            ( PointerPath+            ( Pointer                 [ K "foo"                 , N 0                 , K "bar"                 , N 1+                , K "baz"                 ]-                $ K "baz"             )        it "path end of array" $ do         pointerFromText "/foo/0/bar/1/-"           `shouldBe` Right-            ( PointerPathEnd+            ( Pointer                 [ K "foo"                 , N 0                 , K "bar"                 , N 1+                , E                 ]             )        it "handles implementation-specific numeric parsing" $ do-        pointerFromText "/1e0" `shouldBe` Right (PointerPath [] $ K "1e0")+        pointerFromText "/1e0" `shouldBe` Right (Pointer [K "1e0"])
test/Data/JSON/PatchSpec.hs view
@@ -102,8 +102,9 @@  spec :: Spec spec = do-  context "json-patch-tests main" $ runPatchTests [relfile|tests.json|]-  context "json-patch-tests RFC6092" $ runPatchTests [relfile|spec_tests.json|]+  context "json-patch-tests" $ do+    context "tests.json" $ runPatchTests [relfile|tests.json|]+    context "spec_tests.json" $ runPatchTests [relfile|spec_tests.json|]  decodeFileThrow :: FromJSON a => Path b File -> IO a decodeFileThrow file = do
+ test/Data/JSON/Pointer/TokenSpec.hs view
@@ -0,0 +1,42 @@+module Data.JSON.Pointer.TokenSpec+  ( spec+  ) where++import Prelude++import Data.Either (isLeft)+import Data.Foldable (for_)+import Data.JSON.Pointer.Token+import Test.Hspec++spec :: Spec+spec = do+  describe "tokenFromText" $ do+    it "parses numbers" $ do+      tokenFromText "0" `shouldBe` Right (N 0)++    it "parses end-of-array" $ do+      tokenFromText "-" `shouldBe` Right E++    it "parses object keys" $ do+      tokenFromText "foo" `shouldBe` Right (K "foo")++    it "unescapes ~0 and ~1" $ do+      tokenFromText "fo~0o~1bar" `shouldBe` Right (K "fo~o/bar")++    it "round-trips with tokenToText" $ do+      let ts =+            [ N 0+            , E+            , K "foo"+            , K "fo~o/bar"+            ]++      for_ ts $ \t -> do+        tokenFromText (tokenToText t) `shouldBe` Right t++    it "parses numeric-looking keys as keys" $ do+      tokenFromText "1e0" `shouldBe` Right (K "1e0")++    it "rejects leading zeros" $ do+      tokenFromText "01" `shouldSatisfy` isLeft