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 +3/−1
- source/benchmark/Main.hs +5/−6
- source/library/Argo.hs +8/−0
- source/library/Argo/Class/FromValue.hs +8/−0
- source/library/Argo/Class/ToValue.hs +13/−0
- source/library/Argo/Codec.hs +1/−1
- source/library/Argo/Decode.hs +9/−4
- source/library/Argo/Encode.hs +8/−7
- source/library/Argo/Encoder.hs +9/−0
- source/library/Argo/Json/Value.hs +2/−0
- source/library/Argo/Literal.hs +9/−0
- source/library/Argo/Pointer/Pointer.hs +85/−0
- source/library/Argo/Pointer/Token.hs +67/−0
- source/library/Argo/QuasiQuoter.hs +11/−5
- source/library/Argo/Result.hs +11/−1
- source/library/Argo/Vendor/Builder.hs +1/−0
- source/library/Argo/Vendor/ByteString.hs +2/−0
- source/test-suite/Main.hs +62/−2
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