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 +5/−4
- src/Data/Aeson/Optics/Ext.hs +30/−1
- src/Data/JSON/Patch/Apply.hs +38/−93
- src/Data/JSON/Patch/Error.hs +11/−14
- src/Data/JSON/Patch/Prelude.hs +19/−0
- src/Data/JSON/Patch/Type.hs +1/−3
- src/Data/JSON/Pointer.hs +35/−32
- src/Data/JSON/Pointer/Token.hs +35/−57
- test/Data/Aeson/Optics/ExtSpec.hs +24/−7
- test/Data/JSON/Patch/PointerSpec.hs +7/−6
- test/Data/JSON/PatchSpec.hs +3/−2
- test/Data/JSON/Pointer/TokenSpec.hs +42/−0
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