packages feed

argo 0.2021.11.2 → 0.2021.11.7

raw patch · 18 files changed

+314/−27 lines, 18 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Argo.QuasiQuoter: quoteExp :: String -> Q Exp
+ Argo: Pointer :: [Token] -> Pointer
+ Argo: Token :: Text -> Token
+ Argo: decodePointer :: ByteString -> Result Pointer
+ Argo: encodePointer :: Pointer -> Builder
+ Argo: evaluate :: Pointer -> Value -> Result Value
+ Argo: newtype Pointer
+ Argo: newtype Token
+ Argo: pointer :: QuasiQuoter
+ Argo.Class.FromValue: instance Argo.Class.FromValue.FromValue Argo.Pointer.Pointer.Pointer
+ Argo.Class.ToValue: instance Argo.Class.ToValue.ToValue Argo.Pointer.Pointer.Pointer
+ Argo.Decode: decodePointer :: ByteString -> Result Pointer
+ Argo.Encode: encodePointer :: Pointer -> Builder
+ Argo.Encoder: defaultConfig :: Config
+ Argo.Encoder: run :: Config -> Encoder a -> (a, Builder)
+ Argo.Literal: digitOne :: Word8
+ Argo.Literal: solidus :: Word8
+ Argo.Literal: tilde :: Word8
+ Argo.Pointer.Pointer: Pointer :: [Token] -> Pointer
+ Argo.Pointer.Pointer: atIndex :: Token -> ArrayOf value -> Result value
+ Argo.Pointer.Pointer: atKey :: Token -> ObjectOf value -> Result value
+ Argo.Pointer.Pointer: decode :: Decoder Pointer
+ Argo.Pointer.Pointer: decodeToken :: Decoder Token
+ Argo.Pointer.Pointer: encode :: Pointer -> Encoder ()
+ Argo.Pointer.Pointer: encodeToken :: Token -> Encoder ()
+ Argo.Pointer.Pointer: evaluate :: Pointer -> Value -> Result Value
+ Argo.Pointer.Pointer: fromList :: [Token] -> Pointer
+ Argo.Pointer.Pointer: instance Control.DeepSeq.NFData Argo.Pointer.Pointer.Pointer
+ Argo.Pointer.Pointer: instance GHC.Classes.Eq Argo.Pointer.Pointer.Pointer
+ Argo.Pointer.Pointer: instance GHC.Generics.Generic Argo.Pointer.Pointer.Pointer
+ Argo.Pointer.Pointer: instance GHC.Show.Show Argo.Pointer.Pointer.Pointer
+ Argo.Pointer.Pointer: instance Language.Haskell.TH.Syntax.Lift Argo.Pointer.Pointer.Pointer
+ Argo.Pointer.Pointer: newtype Pointer
+ Argo.Pointer.Pointer: toList :: Pointer -> [Token]
+ Argo.Pointer.Pointer: tokenToIndex :: Token -> Result Int
+ Argo.Pointer.Token: Token :: Text -> Token
+ Argo.Pointer.Token: decode :: Decoder Token
+ Argo.Pointer.Token: encode :: Token -> Encoder ()
+ Argo.Pointer.Token: encodeChar :: BoundedPrim Word8
+ Argo.Pointer.Token: encodeEscape :: Word8 -> BoundedPrim a
+ Argo.Pointer.Token: fromText :: Text -> Token
+ Argo.Pointer.Token: instance Control.DeepSeq.NFData Argo.Pointer.Token.Token
+ Argo.Pointer.Token: instance GHC.Classes.Eq Argo.Pointer.Token.Token
+ Argo.Pointer.Token: instance GHC.Generics.Generic Argo.Pointer.Token.Token
+ Argo.Pointer.Token: instance GHC.Show.Show Argo.Pointer.Token.Token
+ Argo.Pointer.Token: instance Language.Haskell.TH.Syntax.Lift Argo.Pointer.Token.Token
+ Argo.Pointer.Token: newtype Token
+ Argo.Pointer.Token: toText :: Token -> Text
+ Argo.Pointer.Token: unescapeString :: String -> Maybe String
+ Argo.Pointer.Token: unescapeText :: Text -> Maybe Text
+ Argo.QuasiQuoter: asValue :: Value -> Value
+ Argo.QuasiQuoter: pointer :: QuasiQuoter
+ Argo.Result: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Argo.Result.Result a)
+ Argo.Result: instance GHC.Generics.Generic (Argo.Result.Result a)
+ Argo.Result: result :: (String -> b) -> (a -> b) -> Result a -> b

Files

argo.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2  name: argo-version: 0.2021.11.2+version: 0.2021.11.7  build-type: Simple category: JSON@@ -86,6 +86,8 @@         Argo.Literal         Argo.Main         Argo.Pattern+        Argo.Pointer.Pointer+        Argo.Pointer.Token         Argo.QuasiQuoter         Argo.Result     hs-source-dirs: source/library
source/benchmark/Main.hs view
@@ -44,7 +44,7 @@             , Tasty.bench "10000 elements" . Tasty.nf encode . Argo.Object . replicate 10000 $ Argo.Member (Argo.Name "") Argo.Null             ]         ]-    , Tasty.bgroup "decode" $ let decode = resultToMaybe . Argo.decode :: ByteString.ByteString -> Maybe Argo.Value in+    , Tasty.bgroup "decode" $ let decode = Argo.decode :: ByteString.ByteString -> Argo.Result Argo.Value in         [ Tasty.bgroup "Null"             [ Tasty.bench "null" $ Tasty.nf decode "null"             ]@@ -86,9 +86,8 @@             , Tasty.bench "10000 elements" . Tasty.nf decode $ "{\"\":null" <> ByteString.pack (take (5 * 9999) $ cycle [0x2c, 0x22, 0x22, 0x3a, 0x6e, 0x75, 0x6c, 0x6c]) <> "}"             ]         ]+    , Tasty.bgroup "Pointer"+        [ Tasty.bench "decode" $ Tasty.nf Argo.decodePointer ""+        , Tasty.bench "encode" . Tasty.nf Builder.toLazyByteString . Argo.encodePointer $ Argo.Pointer []+        ]     ]--resultToMaybe :: Argo.Result a -> Maybe a-resultToMaybe r = case r of-    Argo.Failure _ -> Nothing-    Argo.Success x -> Just x
source/library/Argo.hs view
@@ -22,7 +22,13 @@     , FromValue.FromValue(fromValue)     , ToValue.ToValue(toValue)     , QuasiQuoter.value+    , QuasiQuoter.pointer     , Result.Result(Failure, Success)+    , Pointer.Pointer(Pointer.Pointer)+    , Token.Token(Token.Token)+    , Pointer.evaluate+    , Encode.encodePointer+    , Decode.decodePointer     ) where  import qualified Argo.Class.FromValue as FromValue@@ -32,6 +38,8 @@ import qualified Argo.Encoder as Encoder import qualified Argo.Json.Name as Name import qualified Argo.Json.Value as Value+import qualified Argo.Pointer.Pointer as Pointer+import qualified Argo.Pointer.Token as Token import qualified Argo.Pattern as Pattern import qualified Argo.QuasiQuoter as QuasiQuoter import qualified Argo.Result as Result
source/library/Argo/Class/FromValue.hs view
@@ -2,9 +2,11 @@  module Argo.Class.FromValue where +import qualified Argo.Decoder as Decoder import qualified Argo.Json.Number as Number import qualified Argo.Json.Value as Value import qualified Argo.Pattern as Pattern+import qualified Argo.Pointer.Pointer as Pointer import qualified Argo.Result as Result import qualified Argo.Vendor.Text as Text import qualified Data.Bits as Bits@@ -109,6 +111,12 @@     fromValue = withObject "Map"         $ fmap Map.fromList         . traverse (\ (Pattern.Member (Pattern.Name k) v) -> (,) k <$> fromValue v)++instance FromValue Pointer.Pointer where+    fromValue = withString "Pointer"+        $ fmap snd+        . Decoder.run (Pointer.decode <* Decoder.eof)+        . Text.encodeUtf8  withBoolean :: String -> (Bool -> Result.Result a) -> Value.Value -> Result.Result a withBoolean s f x = case x of
source/library/Argo/Class/ToValue.hs view
@@ -2,9 +2,13 @@  module Argo.Class.ToValue where +import qualified Argo.Encoder as Encoder import qualified Argo.Json.Number as Number import qualified Argo.Json.Value as Value import qualified Argo.Pattern as Pattern+import qualified Argo.Pointer.Pointer as Pointer+import qualified Argo.Vendor.Builder as Builder+import qualified Argo.Vendor.ByteString as ByteString import qualified Argo.Vendor.Text as Text import qualified Data.Int as Int import qualified Data.List as List@@ -92,6 +96,15 @@     toValue x = Pattern.Object         . fmap (\ (k, v) -> Pattern.Member (Pattern.Name k) (toValue v))         $ Map.toAscList x++instance ToValue Pointer.Pointer where+    toValue = either (error . mappend "Pointer.toValue: " . show) toValue+        . Text.decodeUtf8'+        . ByteString.toStrict+        . Builder.toLazyByteString+        . snd+        . Encoder.run Encoder.defaultConfig+        . Pointer.encode  realFloatToValue :: RealFloat a => a -> Value.Value realFloatToValue x
source/library/Argo/Codec.hs view
@@ -242,7 +242,7 @@         l <- Trans.get         case l of             [] -> Trans.lift $ Trans.throwE "unexpected empty list"-            h : t ->  case decodeWith c h of+            h : t -> case decodeWith c h of                 Result.Failure y -> Trans.lift $ Trans.throwE y                 Result.Success y -> do                     Trans.put t
source/library/Argo/Decode.hs view
@@ -3,11 +3,16 @@ import qualified Argo.Class.FromValue as FromValue import qualified Argo.Decoder as Decoder import qualified Argo.Json.Value as Value+import qualified Argo.Pointer.Pointer as Pointer import qualified Argo.Result as Result import qualified Argo.Vendor.ByteString as ByteString  decode :: FromValue.FromValue a => ByteString.ByteString -> Result.Result a-decode x =-    case Decoder.run (Decoder.spaces *> Value.decode <* Decoder.eof) x of-        Result.Failure e -> Result.Failure e-        Result.Success (_, y) -> FromValue.fromValue y+decode x = do+    (_, y) <- Decoder.run (Decoder.spaces *> Value.decode <* Decoder.eof) x+    FromValue.fromValue y++decodePointer :: ByteString.ByteString -> Result.Result Pointer.Pointer+decodePointer =+    let d = Pointer.decode <* Decoder.eof+    in fmap snd . Decoder.run d
source/library/Argo/Encode.hs view
@@ -4,24 +4,25 @@ import qualified Argo.Encoder as Encoder import qualified Argo.Json.Value as Value import qualified Argo.Literal as Literal+import qualified Argo.Pointer.Pointer as Pointer import qualified Argo.Vendor.Builder as Builder import qualified Argo.Vendor.Transformers as Trans import qualified Control.Monad as Monad-import qualified Data.Functor.Identity as Identity  encode :: ToValue.ToValue a => a -> Builder.Builder encode = encodeWith $ Encoder.Spaces 0  encodeWith :: ToValue.ToValue a => Encoder.Indent -> a -> Builder.Builder encodeWith i x =-    let c = Encoder.Config { Encoder.indent = i, Encoder.level = 0 }-    in snd-    . Identity.runIdentity-    . Trans.runWriterT-    . flip Trans.runReaderT c-    $ do+    let c = Encoder.defaultConfig { Encoder.indent = i }+    in snd . Encoder.run c $ do         Value.encode $ ToValue.toValue x         Monad.when (Encoder.hasIndent c)             . Trans.lift             . Trans.tell             $ Builder.word8 Literal.newLine++encodePointer :: Pointer.Pointer -> Builder.Builder+encodePointer =+    let c = Encoder.defaultConfig+    in snd . Encoder.run c . Pointer.encode
source/library/Argo/Encoder.hs view
@@ -9,10 +9,19 @@  type Encoder = Trans.ReaderT Config (Trans.WriterT Builder.Builder Identity.Identity) +run :: Config -> Encoder a -> (a, Builder.Builder)+run c = Identity.runIdentity . Trans.runWriterT . flip Trans.runReaderT c+ data Config = Config     { indent :: Indent     , level :: Int     } deriving (Eq, Show)++defaultConfig :: Config+defaultConfig = Config+    { indent = Spaces 0+    , level = 0+    }  data Indent     = Spaces Int
source/library/Argo/Json/Value.hs view
@@ -18,6 +18,8 @@ import qualified Argo.Vendor.TemplateHaskell as TH import qualified GHC.Generics as Generics +-- | A JSON (JavaScript Object Notation) value, as described by RFC 8259.+-- <https://datatracker.ietf.org/doc/html/rfc8259> data Value     = Null Null.Null     | Boolean Boolean.Boolean
source/library/Argo/Literal.hs view
@@ -18,6 +18,9 @@ digitNine :: Word.Word8 digitNine = 0x39 +digitOne :: Word.Word8+digitOne = 0x31+ digitZero :: Word.Word8 digitZero = 0x30 @@ -87,8 +90,14 @@ rightSquareBracket :: Word.Word8 rightSquareBracket = 0x5d +solidus :: Word.Word8+solidus = 0x2f+ space :: Word.Word8 space = 0x20++tilde :: Word.Word8+tilde = 0x7e  true :: ByteString.ByteString true = ByteString.pack [0x74, 0x72, 0x75, 0x65]
+ source/library/Argo/Pointer/Pointer.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveLift #-}++module Argo.Pointer.Pointer where++import qualified Argo.Decoder as Decoder+import qualified Argo.Encoder as Encoder+import qualified Argo.Json.Value as Value+import qualified Argo.Json.Object as Object+import qualified Argo.Json.Array as Array+import qualified Argo.Json.Member as Member+import qualified Argo.Json.Name as Name+import qualified Argo.Json.String as String+import qualified Argo.Literal as Literal+import qualified Argo.Pointer.Token as Token+import qualified Argo.Result as Result+import qualified Argo.Vendor.Builder as Builder+import qualified Argo.Vendor.DeepSeq as DeepSeq+import qualified Argo.Vendor.TemplateHaskell as TH+import qualified Argo.Vendor.Transformers as Trans+import qualified Argo.Vendor.Text as Text+import qualified Control.Applicative as Applicative+import qualified Data.List as List+import qualified GHC.Generics as Generics+import qualified Text.Read as Read++-- | A JSON pointer, as described by RFC 6901.+-- <https://datatracker.ietf.org/doc/html/rfc6901>+newtype Pointer+    = Pointer [Token.Token]+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)++fromList :: [Token.Token] -> Pointer+fromList = Pointer++toList :: Pointer -> [Token.Token]+toList (Pointer x) = x++decode :: Decoder.Decoder Pointer+decode = fromList <$> Applicative.many decodeToken++decodeToken :: Decoder.Decoder Token.Token+decodeToken = do+    Decoder.word8 Literal.solidus+    Token.decode++encode :: Pointer -> Encoder.Encoder ()+encode = mapM_ encodeToken . toList++encodeToken :: Token.Token -> Encoder.Encoder ()+encodeToken x = do+    Trans.lift . Trans.tell $ Builder.word8 Literal.solidus+    Token.encode x++evaluate :: Pointer -> Value.Value -> Result.Result Value.Value+evaluate p v = case toList p of+    [] -> pure v+    t : ts -> do+        w <- case v of+            Value.Array a -> atIndex t a+            Value.Object o -> atKey t o+            _ -> fail "not indexable"+        evaluate (fromList ts) w++atIndex :: Token.Token -> Array.ArrayOf value -> Result.Result value+atIndex t a = do+    i <- tokenToIndex t+    case drop i $ Array.toList a of+        [] -> fail $ "missing index: " <> show t+        e : _ -> pure e++tokenToIndex :: Token.Token -> Result.Result Int+tokenToIndex token = do+    let+        text = Token.toText token+        invalid = "invalid index: " <> show token+    case Text.uncons text of+        Just ('0', rest) -> if Text.null rest then pure 0 else fail invalid+        _ -> maybe (fail invalid) pure . Read.readMaybe $ Text.unpack text++atKey :: Token.Token -> Object.ObjectOf value -> Result.Result value+atKey t = maybe (fail $ "missing key: " <> show t) (\ (Member.Member _ v) -> pure v)+    . List.find (\ (Member.Member k _) -> String.toText (Name.toString k) == Token.toText t)+    . Object.toList
+ source/library/Argo/Pointer/Token.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveLift #-}++module Argo.Pointer.Token where++import qualified Argo.Decoder as Decoder+import qualified Argo.Encoder as Encoder+import qualified Argo.Literal as Literal+import qualified Argo.Vendor.Builder as Builder+import qualified Argo.Vendor.DeepSeq as DeepSeq+import qualified Argo.Vendor.TemplateHaskell as TH+import qualified Argo.Vendor.Text as Text+import qualified Argo.Vendor.Transformers as Trans+import qualified Data.Word as Word+import qualified GHC.Generics as Generics++newtype Token+    = Token Text.Text+    deriving (Eq, Generics.Generic, TH.Lift, DeepSeq.NFData, Show)++fromText :: Text.Text -> Token+fromText = Token++toText :: Token -> Text.Text+toText (Token x) = x++decode :: Decoder.Decoder Token+decode = do+    x <- Decoder.takeWhile $ (/=) Literal.solidus+    y <- case Text.decodeUtf8' x of+        Left e -> fail $ show e+        Right y -> pure y+    case unescapeText y of+        Nothing -> fail "invalid escape"+        Just z -> pure $ fromText z++unescapeText :: Text.Text -> Maybe Text.Text+unescapeText = fmap Text.pack . unescapeString . Text.unpack++unescapeString :: String -> Maybe String+unescapeString xs = case xs of+    "" -> pure xs+    x : ys -> case x of+        '~' -> case ys of+            '0' : zs -> ('~' :) <$> unescapeString zs+            '1' : zs -> ('/' :) <$> unescapeString zs+            _ -> fail "invalid escape"+        _ -> (x :) <$> unescapeString ys++encode :: Token -> Encoder.Encoder ()+encode = Trans.lift+    . Trans.tell+    . Text.encodeUtf8BuilderEscaped encodeChar+    . toText++encodeChar :: Builder.BoundedPrim Word.Word8+encodeChar =+    Builder.condB (== Literal.tilde) (encodeEscape Literal.digitZero)+    . Builder.condB (== Literal.solidus) (encodeEscape Literal.digitOne)+    $ Builder.liftFixedToBounded Builder.word8F++encodeEscape :: Word.Word8 -> Builder.BoundedPrim a+encodeEscape x = Builder.liftFixedToBounded+    $ const (Literal.tilde, x)+    Builder.>$< Builder.word8F+    Builder.>*< Builder.word8F
source/library/Argo/QuasiQuoter.hs view
@@ -6,15 +6,21 @@ import qualified Argo.Vendor.TemplateHaskell as TH import qualified Argo.Vendor.Text as Text +pointer :: TH.QuasiQuoter+pointer = TH.QuasiQuoter+    { TH.quoteDec = const $ fail "cannot be used as a declaration"+    , TH.quoteExp = Result.result fail TH.lift . Decode.decodePointer . Text.encodeUtf8 . Text.pack+    , TH.quotePat = const $ fail "cannot be used as a pattern"+    , TH.quoteType = const $ fail "cannot be used as a type"+    }+ value :: TH.QuasiQuoter value = TH.QuasiQuoter     { TH.quoteDec = const $ fail "cannot be used as a declaration"-    , TH.quoteExp = quoteExp+    , TH.quoteExp = Result.result fail TH.lift . fmap asValue . Decode.decode . Text.encodeUtf8 . Text.pack     , TH.quotePat = const $ fail "cannot be used as a pattern"     , TH.quoteType = const $ fail "cannot be used as a type"     } -quoteExp :: String -> TH.Q TH.Exp-quoteExp x = case Decode.decode . Text.encodeUtf8 $ Text.pack x of-    Result.Failure e -> fail e-    Result.Success y -> TH.lift (y :: Value.Value)+asValue :: Value.Value -> Value.Value+asValue = id
source/library/Argo/Result.hs view
@@ -1,11 +1,16 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+ module Argo.Result where +import qualified Argo.Vendor.DeepSeq as DeepSeq import qualified Control.Applicative as Applicative+import qualified GHC.Generics as Generics  data Result a     = Failure String     | Success a-    deriving (Eq, Show)+    deriving (Eq, Generics.Generic, DeepSeq.NFData, Show)  instance Functor Result where     fmap f r = case r of@@ -32,3 +37,8 @@     rx <|> ry = case rx of         Failure _ -> ry         Success _ -> rx++result :: (String -> b) -> (a -> b) -> Result a -> b+result f g r = case r of+    Failure e -> f e+    Success x -> g x
source/library/Argo/Vendor/Builder.hs view
@@ -5,6 +5,7 @@     , Prim.condB     , Builder.integerDec     , Prim.liftFixedToBounded+    , Builder.toLazyByteString     , Builder.word8     , word8F     , Prim.word16HexFixed
source/library/Argo/Vendor/ByteString.hs view
@@ -14,7 +14,9 @@     , ByteString.stripPrefix     , ByteString.take     , ByteString.takeWhileEnd+    , LazyByteString.toStrict     , ByteString.uncons     ) where  import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as LazyByteString
source/test-suite/Main.hs view
@@ -347,6 +347,8 @@             fromValue (Argo.Array [Argo.Boolean False]) @?= Just (False :| [])         , Tasty.testCase "Map Text a" $ do             fromValue (Argo.Object [Argo.Member (Argo.Name "a") $ Argo.Boolean False]) @?= Just (Map.fromList [("a" :: Text.Text, False)])+        , Tasty.testCase "Pointer" $ do+            fromValue (Argo.String "") @?= Just (Argo.Pointer [])         ]     , Tasty.testGroup "toValue"         [ Tasty.testCase "Value" $ do@@ -399,6 +401,8 @@             Argo.toValue (False :| []) @?= Argo.Array [Argo.Boolean False]         , Tasty.testCase "Map Text a" $ do             Argo.toValue (Map.fromList [("a" :: Text.Text, False)]) @?= Argo.Object [Argo.Member (Argo.Name "a") $ Argo.Boolean False]+        , Tasty.testCase "Pointer" $ do+            Argo.toValue (Argo.Pointer []) @?= Argo.String ""         ]     , Tasty.testGroup "quasi quoter"         [ Tasty.testCase "Null" $ do@@ -416,12 +420,12 @@         ]     , Tasty.testGroup "property"         [ property "decode . encode" $ \ x ->-            (resultToMaybe . Argo.decode . LazyByteString.toStrict . Builder.toLazyByteString $ Argo.encode x) === Just (x :: Argo.Value)+            (Argo.decode . LazyByteString.toStrict . Builder.toLazyByteString $ Argo.encode x) === Argo.Success (x :: Argo.Value)         , property "decode . encodeWith" $ \ x ->             (Argo.decode . LazyByteString.toStrict . Builder.toLazyByteString $ Argo.encodeWith Argo.Tab x) === Argo.Success (x :: Argo.Value)         , Tasty.testGroup "fromValue . toValue"             [ property "Value" $ \ x ->-                (resultToMaybe . Argo.fromValue $ Argo.toValue x) === Just (x :: Argo.Value)+                Argo.fromValue (Argo.toValue x) === Argo.Success (x :: Argo.Value)             , property "Bool" $ \ x ->                 Argo.fromValue (Argo.toValue x) === Argo.Success (x :: Bool)             , property "Char" $ \ x ->@@ -476,6 +480,8 @@                 Argo.fromValue (Argo.toValue x) === Argo.Success (x :: NonEmpty Bool)             , property "Map Text a" $ \ x ->                 Argo.fromValue (Argo.toValue x) === Argo.Success (x :: Map.Map Text.Text Bool)+            , property "Pointer" $ \ x ->+                Argo.fromValue (Argo.toValue x) === Argo.Success (x :: Argo.Pointer)             ]         ]     , Tasty.testGroup "Codec"@@ -510,6 +516,52 @@             Codec.decodeWith recordCodec (Argo.Object [Argo.Member (Argo.Name "bool") $ Argo.Boolean False]) @?= Argo.Success (Record False Nothing)             Codec.decodeWith recordCodec (Argo.Object [Argo.Member (Argo.Name "bool") $ Argo.Boolean False, Argo.Member (Argo.Name "text") $ Argo.String ""]) @?= Argo.Success (Record False $ Just "")         ]+    , Tasty.testGroup "Pointer"+        $ let pointer = Argo.Pointer . fmap Argo.Token in+        [ Tasty.testCase "decode" $ do+            let decode = resultToMaybe . Argo.decodePointer+            decode "" @?= Just (pointer [])+            decode "/" @?= Just (pointer [""])+            decode "/a" @?= Just (pointer ["a"])+            decode "/a/b" @?= Just (pointer ["a", "b"])+            decode "/ab" @?= Just (pointer ["ab"])+            decode "/~0" @?= Just (pointer ["~"])+            decode "/~1" @?= Just (pointer ["/"])+            decode "/~01" @?= Just (pointer ["~1"])+            decode "a" @?= Nothing+            decode "/~2" @?= Nothing+        , Tasty.testCase "encode" $ do+            let encode = Builder.toLazyByteString . Argo.encodePointer+            encode (pointer []) @?= ""+            encode (pointer [""]) @?= "/"+            encode (pointer ["a"]) @?= "/a"+            encode (pointer ["a", "b"]) @?= "/a/b"+            encode (pointer ["ab"]) @?= "/ab"+            encode (pointer ["~"]) @?= "/~0"+            encode (pointer ["/"]) @?= "/~1"+            encode (pointer ["~1"]) @?= "/~01"+        , property "decode . encode" $ \ x ->+            (Argo.decodePointer . LazyByteString.toStrict . Builder.toLazyByteString $ Argo.encodePointer x) === Argo.Success (x :: Argo.Pointer)+        , Tasty.testCase "quasi-quoter" $ do+            [Argo.pointer||] @?= pointer []+            [Argo.pointer|/|] @?= pointer [""]+            [Argo.pointer|/a|] @?= pointer ["a"]+            [Argo.pointer|/a/b|] @?= pointer ["a", "b"]+        , Tasty.testCase "evaluate" $ do+            let evaluate p = resultToMaybe . Argo.evaluate p+            evaluate (pointer []) Argo.Null @?= Just Argo.Null+            evaluate (pointer ["a"]) (Argo.Object [Argo.Member (Argo.Name "a") Argo.Null]) @?= Just Argo.Null+            evaluate (pointer ["a", "b"]) (Argo.Object [Argo.Member (Argo.Name "a") $ Argo.Object [Argo.Member (Argo.Name "b") Argo.Null]]) @?= Just Argo.Null+            evaluate (pointer ["0"]) (Argo.Object [Argo.Member (Argo.Name "0") Argo.Null]) @?= Just Argo.Null+            evaluate (pointer ["0"]) (Argo.Array [Argo.Null]) @?= Just Argo.Null+            evaluate (pointer ["0", "1"]) (Argo.Array [Argo.Array [Argo.Boolean False, Argo.Null]]) @?= Just Argo.Null+            evaluate (pointer ["a"]) Argo.Null @?= Nothing+            evaluate (pointer ["a"]) (Argo.Object []) @?= Nothing+            evaluate (pointer ["a"]) (Argo.Array [Argo.Null]) @?= Nothing+            evaluate (pointer ["0"]) (Argo.Array []) @?= Nothing+            evaluate (pointer ["-"]) (Argo.Array []) @?= Nothing+            evaluate (pointer ["00"]) (Argo.Array [Argo.Null]) @?= Nothing+        ]     ]  data Record = Record@@ -584,3 +636,11 @@ instance Tasty.Arbitrary a => Tasty.Arbitrary (NonEmpty a) where     arbitrary = (:|) <$> Tasty.arbitrary <*> Tasty.arbitrary     shrink (x :| xs) = uncurry (:|) <$> Tasty.shrink (x, xs)++instance Tasty.Arbitrary Argo.Pointer where+    arbitrary = Argo.Pointer <$> Tasty.arbitrary+    shrink (Argo.Pointer x) = Argo.Pointer <$> Tasty.shrink x++instance Tasty.Arbitrary Argo.Token where+    arbitrary = Argo.Token <$> Tasty.arbitrary+    shrink (Argo.Token x) = Argo.Token <$> Tasty.shrink x