packages feed

jsonpatch 0.2.0.0 → 0.2.1.0

raw patch · 11 files changed

+113/−20 lines, 11 files

Files

README.lhs view
@@ -63,7 +63,7 @@  ```haskell result :: Either PatchError Value-result = applyPatches patch [aesonQQ|+result = patchValue patch [aesonQQ|   {     "baz": "qux",     "foo": "bar"
README.md view
@@ -63,7 +63,7 @@  ```haskell result :: Either PatchError Value-result = applyPatches patch [aesonQQ|+result = patchValue patch [aesonQQ|   {     "baz": "qux",     "foo": "bar"
jsonpatch.cabal view
@@ -1,6 +1,6 @@ cabal-version:      1.18 name:               jsonpatch-version:            0.2.0.0+version:            0.2.1.0 license:            AGPL-3 license-file:       COPYING maintainer:         Patrick Brisbin@@ -38,6 +38,7 @@         Data.Aeson.Optics.Ext         Data.JSON.Patch         Data.JSON.Patch.Apply+        Data.JSON.Patch.Apply.AsValue         Data.JSON.Patch.Error         Data.JSON.Patch.Prelude         Data.JSON.Patch.Type
src/Data/JSON/Patch.hs view
@@ -9,8 +9,10 @@ module Data.JSON.Patch   ( Patch   , PatchError-  , applyPatches+  , patchValue+  , patchAsValue   ) where  import Data.JSON.Patch.Apply+import Data.JSON.Patch.Apply.AsValue import Data.JSON.Patch.Type
src/Data/JSON/Patch/Apply.hs view
@@ -7,8 +7,8 @@ -- Stability   : experimental -- Portability : POSIX module Data.JSON.Patch.Apply-  ( applyPatches-  , PatchError (..)+  ( PatchError (..)+  , patchValue   ) where  import Data.JSON.Patch.Prelude@@ -21,17 +21,18 @@ import Data.Vector qualified as V import Optics.Core -applyPatches :: [Patch] -> Value -> Either PatchError Value-applyPatches ps v = foldM applyPatch v ps--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+-- | Apply the given 'Patch'es to the given 'Value'+patchValue :: [Patch] -> Value -> Either PatchError Value+patchValue patches target = foldM go target patches+ where+  go :: Value -> Patch -> Either PatchError Value+  go 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  get :: Pointer -> Value -> Either PatchError Value get p val = note (PointerNotFound p Nothing) $ val ^? pointerL p
+ src/Data/JSON/Patch/Apply/AsValue.hs view
@@ -0,0 +1,64 @@+-- |+--+-- Module      : Data.JSON.Patch.Apply.AsValue+-- Copyright   : (c) 2025 Patrick Brisbin+-- License     : AGPL-3+-- Maintainer  : pbrisbin@gmail.com+-- Stability   : experimental+-- Portability : POSIX+module Data.JSON.Patch.Apply.AsValue+  ( PatchError (..)+  , patchAsValue+  ) where++import Data.JSON.Patch.Prelude++import Data.Aeson (FromJSON, Result (..), ToJSON (..), Value (..), fromJSON)+import Data.Aeson.Optics (AsValue (..))+import Data.JSON.Patch.Apply+import Optics.Core++-- | A polymorphic version of 'patchValue'+--+-- The @patch@ input uses 'AsValue' from @aeson-optics@, meaning you can supply+-- a variety of types such as 'ByteString' or 'Value' and it will be parsed into+-- @['Patches']@ (capturing failure as a 'PatchError').+--+-- The @v@ input can be any domain type with JSON instances. We don't use+-- 'AsValue' here as well, even though it provides the same functionality,+-- because it's unlikely your types will have this instance.+--+-- @+-- data Person = Person+--   { name :: Text+--   , age :: Int+--   }+--   deriving stock Generic+--   deriving anyclass (FromJSON, ToJSON)+--+-- patchPersonR :: PersonId -> Handler Person+-- patchPersonR id = do+--   person <- runDB $ get id           -- Person "pat" 19+--   bytes <- getRequestBody            -- "[{op:replace, path:/age, value:21}]"+--+--   case patchAsValue bytes person of+--     Left err -> sendResponse 400 $ displayException err+--     Right updated -> do+--       runDB $ update id updated+--       sendResponse 200 updated       -- Person "pat" 21+-- @+--+-- If the patch creates a value that can't parse back to your domain type, that+-- will also be normalized to 'PatchError'.+patchAsValue+  :: (AsValue patch, FromJSON v, ToJSON v) => patch -> v -> Either PatchError v+patchAsValue p target = do+  pVal <- note (ParseError Null "not JSON") $ p ^? _Value+  patches <- fromJSONEither pVal+  result <- patchValue patches $ toJSON target+  fromJSONEither result++fromJSONEither :: FromJSON a => Value -> Either PatchError a+fromJSONEither v = case fromJSON v of+  Error e -> Left $ ParseError v e+  Success a -> Right a
src/Data/JSON/Patch/Prelude.hs view
@@ -1,3 +1,11 @@+-- |+--+-- Module      : Data.JSON.Patch.Prelude+-- Copyright   : (c) 2025 Patrick Brisbin+-- License     : AGPL-3+-- Maintainer  : pbrisbin@gmail.com+-- Stability   : experimental+-- Portability : POSIX module Data.JSON.Patch.Prelude   ( module X   , note
src/Data/JSON/Patch/Type.hs view
@@ -6,6 +6,8 @@ -- Maintainer  : pbrisbin@gmail.com -- Stability   : experimental -- Portability : POSIX+--+-- <https://datatracker.ietf.org/doc/html/rfc6902> module Data.JSON.Patch.Type   ( Patch (..)   , AddOp (..)
src/Data/JSON/Pointer.hs view
@@ -1,3 +1,12 @@+-- |+--+-- Module      : Data.JSON.Pointer+-- Copyright   : (c) 2025 Patrick Brisbin+-- License     : AGPL-3+-- Maintainer  : pbrisbin@gmail.com+-- Stability   : experimental+-- Portability : POSIX+-- -- <https://datatracker.ietf.org/doc/html/rfc6901/> module Data.JSON.Pointer   ( Pointer (..)
test/Data/JSON/PatchSpec.hs view
@@ -70,9 +70,7 @@       | otherwise = it      comment = fromMaybe ("test #" <> show n) t.comment-    result = case fromJSON t.patch of-      Error err -> Left $ ParseError t.patch err-      Success patches -> applyPatches patches t.doc+    result = patchAsValue t.patch t.doc    it' comment $ case (result, t.expected) of     (Left ex, Left e) -> do
test/Data/JSON/Pointer/TokenSpec.hs view
@@ -1,3 +1,11 @@+-- |+--+-- Module      : Data.JSON.Pointer.TokenSpec+-- Copyright   : (c) 2025 Patrick Brisbin+-- License     : AGPL-3+-- Maintainer  : pbrisbin@gmail.com+-- Stability   : experimental+-- Portability : POSIX module Data.JSON.Pointer.TokenSpec   ( spec   ) where