hjsonpointer 0.3.0.2 → 1.0.0.0
raw patch · 6 files changed
+177/−160 lines, 6 filesdep ~aesonPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson
API changes (from Hackage documentation)
- Data.Aeson.Pointer: instance Data.Aeson.Types.Class.FromJSON Data.Aeson.Pointer.Pointer
- Data.Aeson.Pointer: instance Data.Aeson.Types.Class.ToJSON Data.Aeson.Pointer.Pointer
+ Data.Aeson.Pointer: instance Data.Aeson.Types.FromJSON.FromJSON Data.Aeson.Pointer.Pointer
+ Data.Aeson.Pointer: instance Data.Aeson.Types.ToJSON.ToJSON Data.Aeson.Pointer.Pointer
Files
- Example.hs +12/−12
- README.md +13/−13
- changelog.md +4/−0
- hjsonpointer.cabal +57/−46
- src/Data/Aeson/Pointer.hs +42/−40
- test/Unit.hs +49/−49
Example.hs view
@@ -8,20 +8,20 @@ main :: IO () main = do- -- JSON Pointers must either be empty or start with a /.- pntr1 <- case P.unescape "/foo/0" of- Left _ -> error "Failed to construct JSON Pointer."- Right pntr -> return pntr+ -- JSON Pointers must either be empty or start with a /.+ pntr1 <- case P.unescape "/foo/0" of+ Left _ -> error "Failed to construct JSON Pointer."+ Right pntr -> return pntr - -- We can also write JSON Pointers in Haskell.- let pntr2 = P.Pointer [P.Token "/"]- -- When we do this we don't have to escape / or ~ characters- -- (as ~1 and ~0 respectively) like we do in an escaped JSON- -- Pointer string.- unless (P.unescape "/~1" == Right pntr2) (error "ohno!")+ -- We can also write JSON Pointers in Haskell.+ let pntr2 = P.Pointer [P.Token "/"]+ -- When we do this we don't have to escape / or ~ characters+ -- (as ~1 and ~0 respectively) like we do in an escaped JSON+ -- Pointer string.+ unless (P.unescape "/~1" == Right pntr2) (error "ohno!") - print (P.resolve pntr1 document)- print (P.resolve pntr2 document)+ print (P.resolve pntr1 document)+ print (P.resolve pntr2 document) where document :: Value
README.md view
@@ -9,7 +9,7 @@ ```haskell {-# LANGUAGE OverloadedStrings #-} -module Main where+module Example where import Control.Monad (unless) import Data.Aeson@@ -17,20 +17,20 @@ main :: IO () main = do- -- JSON Pointers must either be empty or start with a /.- pntr1 <- case P.unescape "/foo/0" of- Left _ -> error "Failed to construct JSON Pointer."- Right pntr -> pure pntr+ -- JSON Pointers must either be empty or start with a /.+ pntr1 <- case P.unescape "/foo/0" of+ Left _ -> error "Failed to construct JSON Pointer."+ Right pntr -> return pntr - -- We can also write JSON Pointers in Haskell.- let pntr2 = P.Pointer [P.Token "/"]- -- When we do this we don't have to escape / or ~ characters- -- (as ~1 and ~0 respectively) like we do in an escaped JSON- -- Pointer string.- unless (P.unescape "/~1" == Right pntr2) (error "ohno!")+ -- We can also write JSON Pointers in Haskell.+ let pntr2 = P.Pointer [P.Token "/"]+ -- When we do this we don't have to escape / or ~ characters+ -- (as ~1 and ~0 respectively) like we do in an escaped JSON+ -- Pointer string.+ unless (P.unescape "/~1" == Right pntr2) (error "ohno!") - print (P.resolve pntr1 document)- print (P.resolve pntr2 document)+ print (P.resolve pntr1 document)+ print (P.resolve pntr2 document) where document :: Value
changelog.md view
@@ -1,3 +1,7 @@+# 1.0.0.0+++ Bump aeson.+ # 0.3.0.2 + Switch to hspec for tests.
hjsonpointer.cabal view
@@ -1,52 +1,63 @@-name: hjsonpointer-version: 0.3.0.2-synopsis: JSON Pointer library-homepage: https://github.com/seagreen/hjsonpointer-license: MIT-license-file: MIT-LICENSE.txt-author: Ian Grant Jeffries-maintainer: ian@housejeffries.com-category: Data-build-type: Simple-cabal-version: >=1.10-tested-with: GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1-extra-source-files: changelog.md- README.md+name: hjsonpointer+version: 1.0.0.0+synopsis: JSON Pointer library+homepage: https://github.com/seagreen/hjsonpointer+license: MIT+license-file: MIT-LICENSE.txt+author: Ian Grant Jeffries+maintainer: ian@housejeffries.com+category: Data+build-type: Simple+cabal-version: >=1.10+tested-with: GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1+extra-source-files:+ changelog.md+ README.md library- hs-source-dirs: src- exposed-modules: Data.Aeson.Pointer- default-language: Haskell2010- default-extensions: OverloadedStrings- other-extensions: GeneralizedNewtypeDeriving- ghc-options: -Wall- build-depends: aeson >= 0.7 && < 0.12- , base >= 4.6 && < 4.10- , QuickCheck >= 2.8 && < 2.10- , unordered-containers >= 0.2 && < 0.3- , text >= 1.2 && < 1.3- , vector >= 0.10 && < 0.12+ hs-source-dirs:+ src+ default-language: Haskell2010+ default-extensions:+ OverloadedStrings+ other-extensions:+ GeneralizedNewtypeDeriving+ ghc-options:+ -Wall+ exposed-modules:+ Data.Aeson.Pointer+ build-depends:+ aeson >= 0.7 && < 1.1+ , base >= 4.6 && < 4.10+ , QuickCheck >= 2.8 && < 2.10+ , unordered-containers >= 0.2 && < 0.3+ , text >= 1.2 && < 1.3+ , vector >= 0.10 && < 0.12 test-suite unit- type: exitcode-stdio-1.0- hs-source-dirs: test- ./- main-is: Unit.hs- other-modules: Example- default-language: Haskell2010- ghc-options: -Wall- default-extensions: OverloadedStrings- build-depends: aeson- , base- , hjsonpointer- , QuickCheck- , unordered-containers- , text- , vector- , hspec >= 2.2 && < 2.3- , http-types >= 0.8 && < 0.10- , HUnit >= 1.2 && < 1.4+ hs-source-dirs:+ test+ ./+ default-language: Haskell2010+ default-extensions:+ OverloadedStrings+ ghc-options:+ -Wall+ type: exitcode-stdio-1.0+ main-is: Unit.hs+ other-modules: Example+ build-depends:+ aeson+ , base+ , hjsonpointer+ , QuickCheck+ , unordered-containers+ , text+ , vector+ , hspec >= 2.2 && < 2.3+ , http-types >= 0.8 && < 0.10+ , HUnit >= 1.2 && < 1.4 source-repository head- type: git- location: git://github.com/seagreen/hjsonpointer.git+ type: git+ location: git://github.com/seagreen/hjsonpointer.git
src/Data/Aeson/Pointer.hs view
@@ -17,41 +17,43 @@ import Data.Traversable newtype Pointer- = Pointer { _unPointer :: [Token] }- deriving (Eq, Show, Monoid, Arbitrary)+ = Pointer { _unPointer :: [Token] }+ deriving (Eq, Show, Monoid, Arbitrary) instance FromJSON Pointer where- parseJSON = withText "JSON Pointer" $ \t ->- case unescape t of- Left e -> fail (show e)- Right p -> pure p+ parseJSON = withText "JSON Pointer" $ \t ->+ case unescape t of+ Left e -> fail (show e)+ Right p -> pure p instance ToJSON Pointer where- toJSON = String . escape+ toJSON = String . escape -- | We don't try to distinguish between integer tokens and string -- tokens since all tokens start as strings, and all tokens can -- be used to resolve JSON objects.-newtype Token = Token { _unToken :: Text } deriving (Eq, Show)+newtype Token+ = Token { _unToken :: Text }+ deriving (Eq, Show) instance Arbitrary Token where- arbitrary = Token . T.pack <$> arbitrary+ arbitrary = Token . T.pack <$> arbitrary escape :: Pointer -> Text escape (Pointer []) = "" escape (Pointer ts) =- T.cons '/'- . T.intercalate "/"- . fmap (T.replace "/" "~1" . T.replace "~" "~0" . _unToken)- $ ts+ T.cons '/'+ . T.intercalate "/"+ . fmap (T.replace "/" "~1" . T.replace "~" "~0" . _unToken)+ $ ts -- * Unescaping data FormatError- = InvalidFirstChar- -- ^ JSON Pointers must either be empty or start with a @/@.- | UnescapedTilde- deriving (Eq, Show)+ = InvalidFirstChar+ -- ^ JSON Pointers must either be empty or start with a @/@.+ | UnescapedTilde+ deriving (Eq, Show) -- | JSON Pointers must either be empty or start with a @/@. This means@@ -65,21 +67,21 @@ -- "Network.HTTP.Types.URI.urlDecode" from http-types. unescape :: Text -> Either FormatError Pointer unescape txt =- case T.splitOn "/" txt of- [] -> Right (Pointer [])- "":xs -> Pointer <$> traverse f xs- _ -> Left InvalidFirstChar+ case T.splitOn "/" txt of+ [] -> Right (Pointer [])+ "":xs -> Pointer <$> traverse f xs+ _ -> Left InvalidFirstChar where f :: Text -> Either FormatError Token f t = case unescapeToken t of- Nothing -> Left UnescapedTilde- Just tok -> Right tok+ Nothing -> Left UnescapedTilde+ Just tok -> Right tok -- | For internal use by 'unescape'. unescapeToken :: Text -> Maybe Token unescapeToken t- | not (isValid t) = Nothing- | otherwise = Just . Token . replace $ t+ | not (isValid t) = Nothing+ | otherwise = Just . Token . replace $ t where -- All tildes must be followed by 0s or 1s. isValid :: Text -> Bool@@ -94,11 +96,11 @@ -- * Resolution data ResolutionError- = ObjectLookupFailed- | ArrayIndexInvalid- | ArrayElemNotFound- | ExpectedObjectOrArray- deriving (Eq, Show)+ = ObjectLookupFailed+ | ArrayIndexInvalid+ | ArrayElemNotFound+ | ExpectedObjectOrArray+ deriving (Eq, Show) resolve :: Pointer -> Value -> Either ResolutionError Value resolve (Pointer []) v = Right v@@ -108,15 +110,15 @@ -- resolve an entire pointer at once). resolveToken :: Token -> Value -> Either ResolutionError Value resolveToken tok (Array vs) =- case readMaybe . T.unpack . _unToken $ tok of- Nothing -> Left ArrayIndexInvalid- Just n -> do- when (n < 0) (Left ArrayIndexInvalid)- case vs V.!? n of- Nothing -> Left ArrayElemNotFound- Just res -> Right res+ case readMaybe . T.unpack . _unToken $ tok of+ Nothing -> Left ArrayIndexInvalid+ Just n -> do+ when (n < 0) (Left ArrayIndexInvalid)+ case vs V.!? n of+ Nothing -> Left ArrayElemNotFound+ Just res -> Right res resolveToken tok (Object h) =- case H.lookup (_unToken tok) h of- Nothing -> Left ObjectLookupFailed- Just res -> Right res+ case H.lookup (_unToken tok) h of+ Nothing -> Left ObjectLookupFailed+ Just res -> Right res resolveToken _ _ = Left ExpectedObjectOrArray
test/Unit.hs view
@@ -24,70 +24,70 @@ main :: IO () main = hspec $ do- describe "example" $ do- it "compiles and runs without errors" Example.main- describe "pointers" $ do- it "can be stored as JSON without changing its value" (property roundtrip)- it "can be represented in a JSON string value" jsonString- it "can be represented in a URI fragment identifier" uriFragment+ describe "example" $ do+ it "compiles and runs without errors" Example.main+ describe "pointers" $ do+ it "can be stored as JSON without changing its value" (property roundtrip)+ it "can be represented in a JSON string value" jsonString+ it "can be represented in a URI fragment identifier" uriFragment roundtrip :: P.Pointer -> Bool roundtrip a = Just a == decode (encode a) jsonString :: Assertion jsonString = traverse_ resolvesTo- [ ("" , specExample)- , ("/foo" , Array $ V.fromList ["bar", "baz"])- , ("/foo/0", String "bar")- , ("/" , Number 0)- , ("/a~1b" , Number 1)- , ("/c%d" , Number 2)- , ("/e^f" , Number 3)- , ("/g|h" , Number 4)- , ("/i\\j" , Number 5)- , ("/k\"l" , Number 6)- , ("/ " , Number 7)- , ("/m~0n" , Number 8)- ]+ [ ("" , specExample)+ , ("/foo" , Array $ V.fromList ["bar", "baz"])+ , ("/foo/0", String "bar")+ , ("/" , Number 0)+ , ("/a~1b" , Number 1)+ , ("/c%d" , Number 2)+ , ("/e^f" , Number 3)+ , ("/g|h" , Number 4)+ , ("/i\\j" , Number 5)+ , ("/k\"l" , Number 6)+ , ("/ " , Number 7)+ , ("/m~0n" , Number 8)+ ] uriFragment :: Assertion uriFragment = traverse_ resolvesTo . fmap (first decodeFragment) $- [ ("#" , specExample)- , ("#/foo" , Array $ V.fromList ["bar", "baz"])- , ("#/foo/0", String "bar")- , ("#/" , Number 0)- , ("#/a~1b" , Number 1)- , ("#/c%25d", Number 2)- , ("#/e%5Ef", Number 3)- , ("#/g%7Ch", Number 4)- , ("#/i%5Cj", Number 5)- , ("#/k%22l", Number 6)- , ("#/%20" , Number 7)- , ("#/m~0n" , Number 8)- ]+ [ ("#" , specExample)+ , ("#/foo" , Array $ V.fromList ["bar", "baz"])+ , ("#/foo/0", String "bar")+ , ("#/" , Number 0)+ , ("#/a~1b" , Number 1)+ , ("#/c%25d", Number 2)+ , ("#/e%5Ef", Number 3)+ , ("#/g%7Ch", Number 4)+ , ("#/i%5Cj", Number 5)+ , ("#/k%22l", Number 6)+ , ("#/%20" , Number 7)+ , ("#/m~0n" , Number 8)+ ] where decodeFragment :: Text -> Text decodeFragment = T.drop 1 . decodeUtf8 . urlDecode True . encodeUtf8 resolvesTo :: (Text, Value) -> Assertion resolvesTo (t, expected) =- case P.unescape t of- Left e -> assertFailure (show e <> " error for pointer: " <> show t)- Right p -> assertEqual- ("Resolved value for pointer: " <> show t)- (Right expected)- (P.resolve p specExample)+ case P.unescape t of+ Left e -> assertFailure (show e <> " error for pointer: " <> show t)+ Right p -> assertEqual+ ("Resolved value for pointer: " <> show t)+ (Right expected)+ (P.resolve p specExample) specExample :: Value specExample = object- [ "foo" .= (["bar", "baz"] :: [Text])- , "" .= (0 :: Int)- , "a/b" .= (1 :: Int)- , "c%d" .= (2 :: Int)- , "e^f" .= (3 :: Int)- , "g|h" .= (4 :: Int)- , "i\\j" .= (5 :: Int)- , "k\"l" .= (6 :: Int)- , " " .= (7 :: Int)- , "m~n" .= (8 :: Int)- ]+ [ "foo" .= (["bar", "baz"] :: [Text])+ , "" .= (0 :: Int)+ , "a/b" .= (1 :: Int)+ , "c%d" .= (2 :: Int)+ , "e^f" .= (3 :: Int)+ , "g|h" .= (4 :: Int)+ , "i\\j" .= (5 :: Int)+ , "k\"l" .= (6 :: Int)+ , " " .= (7 :: Int)+ , "m~n" .= (8 :: Int)+ ]