-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathParsePatch.hs
80 lines (67 loc) · 2.54 KB
/
ParsePatch.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
module ParsePatch (Operation(..), Ix(..), parsePatchFile, parsePatches)
where
import Control.Applicative
import Control.Monad
import Data.Aeson
import Data.Char
import Data.Maybe
import Data.Monoid
import Data.Text (Text(..))
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.ByteString.Lazy.Char8 as BS
data Patch = Patch { op :: Text,
path :: Text,
from :: Maybe Text,
value :: Maybe Value
}
instance FromJSON Patch where
parseJSON (Object v) = Patch <$>
v .: "op" <*>
v .: "path" <*>
v .:? "from" <*>
v .:? "value"
parseJSON _ = mzero
-- |Parses a file of patches, throws an error if it can't parse
parsePatchFile :: String -> IO [Operation]
parsePatchFile file = do
parsed <- decode <$> BS.readFile file
let ops = join $ sequence <$> map patchToOp <$> parsed
case ops of
(Just xs) -> return xs
Nothing -> error $ "File " <> file <> " contains invalid patches"
-- |Parses any given ByteString, either returns a list of Operations or a String error message
parsePatches :: BS.ByteString -> Either String [Operation]
parsePatches str = case decode str >>= mapM patchToOp of
(Just p) -> Right p
Nothing -> Left $ "Can't parse string: " <> BS.unpack str
toPath :: Text -> [Ix]
toPath "" = [K ""]
toPath "/" = []
toPath ps | T.findIndex (=='/') ps == Just 0 = map tToIx $ tail $ T.split (=='/') ps
| otherwise = map tToIx $ T.split (=='/') ps
tToIx t | T.all isDigit t && not (T.null t) = N $ read $ T.unpack t
-- Handle forward slash literals
-- http://tools.ietf.org/html/rfc6901
| otherwise = K (T.replace "~0" "~" (T.replace "~1" "/" t))
-- |Datatype for indexing through JSON values
data Ix = N Int | K Text
instance Show Ix where
show (N n) = show n
show (K t) = T.unpack t
-- |Datatype to store Operations
data Operation = Add [Ix] Value
| Rem [Ix]
| Cop [Ix] [Ix]
| Mov [Ix] [Ix]
| Rep [Ix] Value
| Tes [Ix] Value
deriving (Show)
patchToOp p = case op p of
"add" -> Add (toPath $ path p) <$> value p
"replace" -> Rep (toPath $ path p) <$> value p
"test" -> Tes (toPath $ path p) <$> value p
"copy" -> flip Cop (toPath $ path p) <$> toPath <$> from p
"move" -> flip Mov (toPath $ path p) <$> toPath <$> from p
"remove" -> Just $ Rem $ toPath $ path p
_ -> Nothing