module Data.Aeson.Patch (
Patch(..),
Operation(..),
) where
import Control.Applicative
import Control.Monad
import Data.Aeson
import Data.Aeson.Types
import qualified Data.ByteString.Lazy.Char8 as BS
import Data.Monoid
import Data.Vector (Vector)
import qualified Data.Vector as V
import Data.Aeson.Pointer
newtype Patch = Patch
{ patchOperations :: [Operation] }
deriving (Eq, Show, Monoid)
instance ToJSON Patch where
toJSON (Patch ops) = toJSON ops
instance FromJSON Patch where
parseJSON = modifyFailure ("Could not parse patch: " <> ) . parsePatch
where
parsePatch (Array v) = Patch <$> mapM parseJSON (V.toList v)
parsePatch v = typeMismatch "Array" v
data Operation
= Add { changePointer :: Pointer, changeValue :: Value }
| Rem { changePointer :: Pointer }
| Rep { changePointer :: Pointer, changeValue :: Value }
| Mov { changePointer :: Pointer, fromPointer :: Pointer }
| Cpy { changePointer :: Pointer, fromPointer :: Pointer }
| Tst { changePointer :: Pointer, changeValue :: Value }
deriving (Eq, Show)
instance ToJSON Operation where
toJSON (Add p v) = object
[ ("op", "add")
, "path" .= p
, "value" .= v
]
toJSON (Rem p) = object
[ ("op", "remove")
, "path" .= p
]
toJSON (Rep p v) = object
[ ("op", "replace")
, "path" .= p
, "value" .= v
]
toJSON (Mov p f) = object
[ ("op", "move")
, "path" .= p
, "from" .= f
]
toJSON (Cpy p f) = object
[ ("op", "copy")
, "path" .= p
, "from" .= f
]
toJSON (Tst p v) = object
[ ("op", "test")
, "path" .= p
, "value" .= v
]
instance FromJSON Operation where
parseJSON = parse
where
parse o@(Object v)
= (op v "add" *> (Add <$> v .: "path" <*> v .: "value"))
<|> (op v "replace" *> (Rep <$> v .: "path" <*> v .: "value"))
<|> (op v "move" *> (Mov <$> v .: "path" <*> v .: "from"))
<|> (op v "copy" *> (Cpy <$> v .: "path" <*> v .: "from"))
<|> (op v "test" *> (Tst <$> v .: "path" <*> v .: "value"))
<|> (op v "remove" *> (Rem <$> v .: "path"))
<|> fail ("Expected a JSON patch operation, encountered: " <> BS.unpack (encode o))
parse v = typeMismatch "Operation" v
op v n = fixed v "op" (String n)
fixed o n val = do
v' <- o .: n
if v' == val
then return v'
else mzero
fixed' o n val = (o .: n) >>= \v -> guard (v == n)