packages feed

aeson-match-qq 1.3.2 → 1.4.0

raw patch · 7 files changed

+39/−274 lines, 7 filesdep −case-insensitivedep −containers

Dependencies removed: case-insensitive, containers

Files

CHANGELOG.markdown view
@@ -1,17 +1,7 @@-1.3.2-=====--  * Improved matching errors for embedded Haskell values (https://github.com/supki/aeson-match-qq/pull/18)-- * Separate error types for type- and value- level mismatches (https://github.com/supki/aeson-match-qq/pull/18)-- * Case insensitive strings (https://github.com/supki/aeson-match-qq/pull/17)--1.3.1+1.4.0 ===== -  * Unordered arrays (https://github.com/supki/aeson-match-qq/pull/15) -  * Proper Unicode support (https://github.com/supki/aeson-match-qq/pull/14)+  * Aeson 2.0 compatibility  1.3.0 =====
aeson-match-qq.cabal view
@@ -1,18 +1,20 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.7.+-- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack+--+-- hash: eeb53a9a8a4433aa06146edfc817e03b1cef449f46c1866e6302e4f9bdfee782  name:           aeson-match-qq-version:        1.3.2+version:        1.4.0 synopsis:       Declarative JSON matchers. description:    See README.markdown category:       Web homepage:       https://github.com/supki/aeson-match-qq#readme bug-reports:    https://github.com/supki/aeson-match-qq/issues maintainer:     matvey.aksenov@gmail.com-copyright:      Matvey Aksenov 2022+copyright:      Matvey Aksenov 2021 license:        BSD2 license-file:   LICENSE build-type:     Simple@@ -40,8 +42,6 @@     , attoparsec     , base >=4.14 && <5     , bytestring-    , case-insensitive-    , containers     , either     , haskell-src-meta     , scientific
src/Aeson/Match/QQ.hs view
@@ -12,43 +12,24 @@   , qq   , match   , mismatch-  , mistype   , missingPathElem   , extraArrayValues   , extraObjectValues   ) where  import           Data.String (IsString(..))-import qualified Data.Text.Encoding as Text import           Language.Haskell.TH.Quote (QuasiQuoter(..)) import           Language.Haskell.TH.Syntax (Lift(..)) -import           Aeson.Match.QQ.Internal.Match-  ( Path-  , PathElem(..)-  , match-  , mismatch-  , mistype-  , missingPathElem-  , extraArrayValues-  , extraObjectValues-  )+import           Aeson.Match.QQ.Internal.Match (Path, PathElem(..), match, mismatch, missingPathElem, extraArrayValues, extraObjectValues) import           Aeson.Match.QQ.Internal.Parse (parse)-import           Aeson.Match.QQ.Internal.Value-  ( Value(..)-  , Box(..)-  , Array-  , Object-  , TypeSig(..)-  , Type(..)-  , Nullable(..)-  )+import           Aeson.Match.QQ.Internal.Value (Value(..), Box(..), Array, Object, TypeSig(..), Type(..), Nullable(..))   qq :: QuasiQuoter qq = QuasiQuoter   { quoteExp = \str ->-      case parse (Text.encodeUtf8 (fromString str)) of+      case parse (fromString str) of         Left err ->           error ("Aeson.Match.QQ.qq: " ++ err)         Right val ->
src/Aeson/Match/QQ/Internal/Match.hs view
@@ -4,88 +4,68 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ViewPatterns #-} module Aeson.Match.QQ.Internal.Match where  import           Control.Applicative (liftA2) import           Control.Monad (unless) import           Data.Aeson ((.=)) import qualified Data.Aeson as Aeson-import           Data.Bool (bool)-import qualified Data.CaseInsensitive as CI-import           Data.Either.Validation (Validation(..), eitherToValidation)-import           Data.Foldable (for_, toList)+import qualified Data.Aeson.KeyMap as Aeson (toHashMapText)+import           Data.Either.Validation (Validation, eitherToValidation)+import           Data.Foldable (for_) import           Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap import           Data.List.NonEmpty (NonEmpty)-import           Data.Maybe (mapMaybe)-import qualified Data.Set as Set import           Data.String (IsString(..)) import           Data.Text (Text) import           Data.Vector (Vector) import qualified Data.Vector as Vector import           Prelude hiding (any, null) -import           Aeson.Match.QQ.Internal.Value-  ( Value(..)-  , Box(..)-  , TypeSig(..)-  , Type(..)-  , Nullable(..)-  , embed-  )+import           Aeson.Match.QQ.Internal.Value (Value(..), Box(..), TypeSig(..), Type(..), Nullable(..))  -match-  :: Value Aeson.Value-  -> Aeson.Value-  -> Validation (NonEmpty VE) (HashMap Text Aeson.Value)+match :: Value Aeson.Value -> Aeson.Value -> Validation (NonEmpty VE) (HashMap Text Aeson.Value) match =   go []  where   go path matcher given = do     let mismatched = mismatch (reverse path) matcher given-        mistyped = mistype (reverse path) matcher given     case (matcher, given) of       (Any holeTypeO nameO, val) -> do         for_ holeTypeO $ \holeType ->           unless (holeTypeMatch holeType val)-            mistyped+            mismatched         pure (maybe mempty (\name -> HashMap.singleton name val) nameO)       (Null, Aeson.Null) ->         pure mempty       (Null, _) -> do-        mistyped+        mismatched         pure mempty       (Bool b, Aeson.Bool b') -> do         unless (b == b') mismatched         pure mempty       (Bool _, _) -> do-        mistyped+        mismatched         pure mempty       (Number n, Aeson.Number n') -> do         unless (n == n') mismatched         pure mempty       (Number _, _) -> do-        mistyped+        mismatched         pure mempty       (String str, Aeson.String str') -> do         unless (str == str') mismatched         pure mempty       (String _, _) -> do-        mistyped-        pure mempty-      (StringCI str, Aeson.String str') -> do-        unless (str == CI.mk str') mismatched-        pure mempty-      (StringCI _, _) -> do-        mistyped+        mismatched         pure mempty       (Array Box {knownValues, extendable}, Aeson.Array arr) ->-        let-          fold f =-            Vector.ifoldr (\i v a -> liftA2 HashMap.union a (f i v)) (pure mempty)-          extraValues =-            Vector.drop (Vector.length knownValues) arr+        let fold f =+              Vector.ifoldr (\i v a -> liftA2 HashMap.union a (f i v)) (pure mempty)+            extraValues =+              Vector.drop (Vector.length knownValues) arr         in           unless             (extendable || Vector.null extraValues)@@ -94,14 +74,9 @@             (\i v -> maybe (missingPathElem (reverse path) (Idx i)) (go (Idx i : path) v) (arr Vector.!? i))             knownValues       (Array _, _) -> do-        mistyped-        pure mempty-      (ArrayUO box, Aeson.Array arr) ->-        matchArrayUO mismatched path box arr-      (ArrayUO _, _) -> do-        mistyped+        mismatched         pure mempty-      (Object Box {knownValues, extendable}, Aeson.Object o) ->+      (Object Box {knownValues, extendable}, Aeson.Object (Aeson.toHashMapText -> o)) ->         let fold f =               HashMap.foldrWithKey (\k v a -> liftA2 HashMap.union a (f k v)) (pure mempty)             extraValues =@@ -114,10 +89,11 @@             (\k v -> maybe (missingPathElem (reverse path) (Key k)) (go (Key k : path) v) (HashMap.lookup k o))             knownValues       (Object _, _) -> do-        mistyped+        mismatched         pure mempty-      (Ext val, val') ->-        go path (embed val) val'+      (Ext val, val') -> do+        unless (val == val') mismatched+        pure mempty  holeTypeMatch :: TypeSig -> Aeson.Value -> Bool holeTypeMatch type_ val =@@ -126,78 +102,14 @@     (TypeSig {type_ = BoolT} , Aeson.Bool {}) -> True     (TypeSig {type_ = NumberT} , Aeson.Number {}) -> True     (TypeSig {type_ = StringT} , Aeson.String {}) -> True-    (TypeSig {type_ = StringCIT} , Aeson.String {}) -> True     (TypeSig {type_ = ArrayT} , Aeson.Array {}) -> True-    (TypeSig {type_ = ArrayUOT} , Aeson.Array {}) -> True     (TypeSig {type_ = ObjectT} , Aeson.Object {}) -> True     (_, _) -> False -matchArrayUO-  :: Validation (NonEmpty VE) (HashMap Text Aeson.Value)-  -> Path-  -> Box (Vector (Value Aeson.Value))-  -> Vector Aeson.Value-  -> Validation (NonEmpty VE) (HashMap Text Aeson.Value)-matchArrayUO mismatched path Box {knownValues, extendable} xs = do-  -- Collect possible indices in `xs` for each position in `knownValues`.-  let indices = map (collectMatchingIndices (toList xs)) (toList knownValues)-  -- Find all unique valid ways to map each position in `knownValues` to-  -- a member of `xs`.-  case allIndicesAssignments indices of-    -- If no assignment has been found, we give up.-    [] ->-      mismatched-    ivs : _-      -- If some positions in `knownValues` cannot be mapped to-      -- anything in `xs`, we give up.-      | length ivs < length knownValues ->-        mismatched-      -- If there are some members of `xs` that aren't matched by-      -- anything in `knownValues`, we check if the pattern is-      -- extendable.-      | length ivs < length xs && not extendable -> do-        let is = Set.fromList (map fst ivs)-            extraValues = Vector.ifilter (\i _ -> not (i `Set.member` is)) xs-        extraArrayValues (reverse path) extraValues-      | otherwise ->-        pure (foldMap snd ivs)- where-  collectMatchingIndices is knownValue =-    imapMaybe matchingIndex is-   where-    matchingIndex i x =-      case match knownValue x of-        Success vs ->-          Just (i, vs)-        Failure _ ->-          Nothing-  allIndicesAssignments = map (map unI) . cleanUp . go Set.empty-   where-    go _ [] = [[]]-    go known (is : iss) = do-      (i, vs) <- is-      bool (map (I (i, vs) :) (go (Set.insert i known) iss)) [] (i `Set.member` known)-    cleanUp =-      toList . Set.fromList . map (Set.toAscList . Set.fromList)--newtype I = I { unI :: (Int, HashMap Text Aeson.Value) }--instance Eq I where-  I (a, _) == I (b, _) =-    a == b--instance Ord I where-  I (a, _) `compare` I (b, _) =-    a `compare` b- mismatch :: Path -> Value Aeson.Value -> Aeson.Value -> Validation (NonEmpty VE) a mismatch path matcher given =   throwE (Mismatch MkMismatch {..}) -mistype :: Path -> Value Aeson.Value -> Aeson.Value -> Validation (NonEmpty VE) a-mistype path matcher given =-  throwE (Mistype MkMismatch {..})- missingPathElem :: Path -> PathElem -> Validation (NonEmpty VE) a missingPathElem path missing =   throwE (MissingPathElem MkMissingPathElem {..})@@ -216,7 +128,6 @@  data VE   = Mismatch Mismatch-  | Mistype Mismatch   | MissingPathElem MissingPathElem   | ExtraArrayValues ExtraArrayValues   | ExtraObjectValues ExtraObjectValues@@ -229,10 +140,6 @@         [ "type" .= ("mismatch" :: Text)         , "value" .= v         ]-      Mistype v ->-        [ "type" .= ("mistype" :: Text)-        , "value" .= v-        ]       MissingPathElem v ->         [ "type" .= ("missing-path-elem" :: Text)         , "value" .= v@@ -313,7 +220,3 @@ instance IsString PathElem where   fromString =     Key . fromString--imapMaybe :: (Int -> a -> Maybe b) -> [a] -> [b]-imapMaybe f =-  mapMaybe (uncurry f) . zip [0..]
src/Aeson/Match/QQ/Internal/Parse.hs view
@@ -7,12 +7,11 @@  import           Control.Applicative ((<|>), optional) import qualified Data.Aeson.Parser as Aeson-import qualified Data.Attoparsec.ByteString as Atto import qualified Data.ByteString as ByteString -- cannot use .Text here due to .Aeson parsers being tied to .ByteString+import qualified Data.Attoparsec.ByteString as Atto import           Data.Bool (bool) import           Data.ByteString (ByteString)-import qualified Data.CaseInsensitive as CI import qualified Data.Char as Char import           Data.Foldable (asum) import qualified Data.HashMap.Strict as HashMap@@ -50,8 +49,6 @@       string     OpenSquareBracketP ->       array-    OpenParenP ->-      arrayUO <|> stringCI     OpenCurlyBracketP ->       object     HashP ->@@ -92,12 +89,6 @@ string =   fmap String Aeson.jstring -stringCI :: Atto.Parser (Value Exp)-stringCI = do-  _ <- Atto.string "(ci)"-  spaces-  fmap (StringCI . CI.mk) Aeson.jstring- array :: Atto.Parser (Value Exp) array = do   _ <- Atto.word8 OpenSquareBracketP@@ -137,13 +128,6 @@       _ ->         error "impossible" -arrayUO :: Atto.Parser (Value Exp)-arrayUO = do-  _ <- Atto.string "(unordered)"-  spaces-  Array box <- array-  pure (ArrayUO box)- object :: Atto.Parser (Value Exp) object = do   _ <- Atto.word8 OpenCurlyBracketP@@ -212,9 +196,7 @@     [ p "bool" BoolT     , p "number" NumberT     , p "string" StringT-    , p "ci-string" StringCIT     , p "array" ArrayT-    , p "unordered-array" ArrayUOT     , p "object" ObjectT     ]  where@@ -244,15 +226,9 @@ pattern OpenSquareBracketP = 91 -- '[' pattern CloseSquareBracketP = 93 -- ']' -pattern OpenParenP :: Word8-pattern OpenParenP = 40 -- '('--- pattern CloseParenP :: Word8--- pattern CloseParenP = 41 -- ')'- pattern OpenCurlyBracketP, CloseCurlyBracketP, ColonP :: Word8 pattern OpenCurlyBracketP = 123 -- '{' pattern CloseCurlyBracketP = 125 -- '}'- pattern ColonP = 58 -- ':'  pattern ZeroP, NineP, MinusP :: Word8
src/Aeson/Match/QQ/Internal/Value.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE StrictData #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ViewPatterns #-} module Aeson.Match.QQ.Internal.Value   ( Value(..)   , Box(..)@@ -15,14 +14,11 @@   , TypeSig(..)   , Type(..)   , Nullable(..)-  , embed   ) where  import           Data.Aeson ((.=)) import qualified Data.Aeson as Aeson import qualified Data.Aeson.Encoding.Internal as Aeson (encodingToLazyByteString)-import           Data.CaseInsensitive (CI)-import qualified Data.CaseInsensitive as CI import           Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap import           Data.Scientific (Scientific)@@ -32,7 +28,7 @@ import           Data.Vector (Vector) import qualified Data.Vector as Vector import           Language.Haskell.TH (Exp(..), Lit(..))-import           Language.Haskell.TH.Syntax (Lift(..))+import           Language.Haskell.TH.Syntax (Lift(..), unsafeTExpCoerce) import           Prelude hiding (any, null)  @@ -42,9 +38,7 @@   | Bool Bool   | Number Scientific   | String Text-  | StringCI (CI Text)   | Array (Array ext)-  | ArrayUO (Array ext)   | Object (Object ext)   | Ext ext     deriving (Show, Eq)@@ -72,18 +66,10 @@         [ "type" .= ("string" :: Text)         , "value" .= v         ]-      StringCI v ->-        [ "type" .= ("string-ci" :: Text)-        , "value" .= CI.original v-        ]       Array v ->         [ "type" .= ("array" :: Text)         , "value" .= v         ]-      ArrayUO v ->-        [ "type" .= ("array-unordered" :: Text)-        , "value" .= v-        ]       Object v ->         [ "type" .= ("object" :: Text)         , "value" .= v@@ -124,8 +110,6 @@       [| Number (fromRational $(pure (LitE (RationalL (toRational n))))) :: Value Aeson.Value |]     String str ->       [| String (fromString $(pure (LitE (textL str)))) :: Value Aeson.Value |]-    StringCI str ->-      [| StringCI (fromString $(pure (LitE (textL (CI.original str))))) :: Value Aeson.Value |]     Array Box {knownValues, extendable} -> [|         Array Box           { knownValues =@@ -133,13 +117,6 @@           , extendable           } :: Value Aeson.Value       |]-    ArrayUO Box {knownValues, extendable} -> [|-        ArrayUO Box-          { knownValues =-              Vector.fromList $(fmap (ListE . Vector.toList) (traverse lift knownValues))-          , extendable-          } :: Value Aeson.Value-      |]     Object Box {knownValues, extendable} -> [|         Object Box           { knownValues =@@ -152,6 +129,8 @@    where     textL =       StringL . Text.unpack+  liftTyped =+    unsafeTExpCoerce . lift  data TypeSig = TypeSig   { type_    :: Type@@ -169,9 +148,7 @@   = BoolT   | NumberT   | StringT-  | StringCIT   | ArrayT-  | ArrayUOT   | ObjectT     deriving (Show, Eq, Lift) @@ -181,9 +158,7 @@       BoolT {} -> "bool" :: Text       NumberT {} -> "number"       StringT {} -> "string"-      StringCIT {} -> "string-ci"       ArrayT {} -> "array"-      ArrayUOT {} -> "array-unordered"       ObjectT {} -> "object"  data Nullable@@ -196,18 +171,3 @@     Aeson.toJSON . \case       Nullable -> True       NonNullable -> False--embed :: Aeson.Value -> Value ext-embed = \case-  Aeson.Null ->-    Null-  Aeson.Bool b ->-    Bool b-  Aeson.Number n ->-    Number n-  Aeson.String n ->-    String n-  Aeson.Array xs ->-    Array Box {knownValues = fmap embed xs, extendable = False}-  Aeson.Object o ->-    Object Box {knownValues = fmap embed o, extendable = False}
test/Aeson/Match/QQSpec.hs view
@@ -41,15 +41,6 @@       [qq| [1, _, 3, ...] |] `shouldBe`         Array Box {knownValues = [Number 1, Any Nothing Nothing, Number 3], extendable = True} -      [qq| (unordered) [] |] `shouldBe`-        ArrayUO Box {knownValues = [], extendable = False}-      [qq| (unordered) [1, 2, 3] |] `shouldBe`-        ArrayUO Box {knownValues = [Number 1, Number 2, Number 3], extendable = False}-      [qq| (unordered) [1, _, 3] |] `shouldBe`-        ArrayUO Box {knownValues = [Number 1, Any Nothing Nothing, Number 3], extendable = False}-      [qq| (unordered) [1, _, 3, ...] |] `shouldBe`-        ArrayUO Box {knownValues = [Number 1, Any Nothing Nothing, Number 3], extendable = True}-       [qq| {} |] `shouldBe`         Object Box {knownValues = [], extendable = False}       [qq| {foo: 4} |] `shouldBe`@@ -72,19 +63,9 @@       [qq| false |] `shouldMatch` [aesonQQ| false |]       [qq| 4 |] `shouldMatch` [aesonQQ| 4 |]       [qq| "foo" |] `shouldMatch` [aesonQQ| "foo" |]-      [qq| (ci) "foo" |] `shouldMatch` [aesonQQ| "Foo" |]       [qq| [1, 2, 3] |] `shouldMatch` [aesonQQ| [1, 2, 3] |]       [qq| [1, _ : number, 3, ...] |] `shouldMatch` [aesonQQ| [1, 2, 3, 4] |]       [qq| [1, _ : string] |] `shouldMatch` [aesonQQ| [1, "foo"] |]-      [qq| [1, _ : ci-string] |] `shouldMatch` [aesonQQ| [1, "foo"] |]-      [qq| [1, _ : unordered-array] |] `shouldMatch` [aesonQQ| [1, ["foo"]] |]-      [qq| (unordered) [] |] `shouldMatch` [aesonQQ| [] |]-      [qq| (unordered) [1, 2, 3] |] `shouldMatch` [aesonQQ| [1, 2, 3] |]-      [qq| (unordered) [1, 2, 3] |] `shouldMatch` [aesonQQ| [2, 3, 1] |]-      [qq| (unordered) [1, 2, 2] |] `shouldMatch` [aesonQQ| [2, 2, 1] |]-      [qq| (unordered) [1, _, 2] |] `shouldMatch` [aesonQQ| [2, 2, 1] |]-      [qq| (unordered) [1, 2, ...] |] `shouldMatch` [aesonQQ| [2, 3, 1] |]-      [qq| (unordered) [1, 2, ...] |] `shouldMatch` [aesonQQ| [2, 2, 1] |]       [qq| {foo: 4, bar: 7} |] `shouldMatch` [aesonQQ| {foo: 4, bar: 7} |]       [qq| {foo: 4, bar: 7, ...} |] `shouldMatch` [aesonQQ| {foo: 4, bar: 7, baz: 11} |]       [qq| #{1 + 2 :: Int} |] `shouldMatch` [aesonQQ| 3 |]@@ -96,20 +77,20 @@       [qq| false |] `shouldNotMatch` [aesonQQ| true |]       [qq| 4 |] `shouldNotMatch` [aesonQQ| 7 |]       [qq| "foo" |] `shouldNotMatch` [aesonQQ| "bar" |]-      [qq| (ci) "foo" |] `shouldNotMatch` [aesonQQ| "Bar" |]       [qq| [1, 2, 3] |] `shouldNotMatch` [aesonQQ| [1, 2, 3, 4] |]       [qq| [1, 2, 3, ...] |] `shouldNotMatch` [aesonQQ| [1, 2] |]       [qq| [1, _ : string] |] `shouldNotMatch` [aesonQQ| [1, 2] |]       [qq| [1, 2, 3, ...] |] `shouldNotMatch` [aesonQQ| [1, 2, 4] |]-      [qq| (unordered) [1, 2, 3] |] `shouldNotMatch` [aesonQQ| [1, 2, 4] |]-      [qq| (unordered) [1, 2, 3, 4] |] `shouldNotMatch` [aesonQQ| [1, 2, 3] |]-      [qq| (unordered) [1, 2] |] `shouldNotMatch` [aesonQQ| [1, 2, 2] |]       [qq| {foo: 4, bar: 7} |] `shouldNotMatch` [aesonQQ| {foo: 7, bar: 4} |]       [qq| {foo: 4, bar: 7} |] `shouldNotMatch` [aesonQQ| {foo: 4, baz: 7} |]       [qq| {foo: 4, bar: 7, ...} |] `shouldNotMatch` [aesonQQ| {foo: 4, baz: 11} |]       [qq| #{1 + 2 :: Int} |] `shouldNotMatch` [aesonQQ| 4 |]       [qq| {foo: _ : number, bar: 7} |] `shouldNotMatch` [aesonQQ| {foo: "foo", bar: 7} |]       [qq| {foo: _ : number, bar: 7} |] `shouldNotMatch` [aesonQQ| {foo: null, bar: 7} |]+      [qq|+        { foo: _ : string+        , bar: 7+        } |] `shouldNotMatch` [aesonQQ| {foo: null, bar: 7} |]      it "paths" $ do       match [qq| {foo: {bar: {baz: [1, 4]}}} |] [aesonQQ| {foo: {bar: {baz: [1, 7]}}} |] `shouldBe`@@ -119,13 +100,6 @@       match [qq| {foo: _hole} |] [aesonQQ| {foo: {bar: {baz: [1, 4]}}} |] `shouldBe`         pure (HashMap.singleton "hole" [aesonQQ| {bar: {baz: [1, 4]}} |]) -    context "unordered array" $-      it "named holes" $ do-        match [qq| (unordered) [1, _hole] |] [aesonQQ| [2, 1] |] `shouldBe`-          pure (HashMap.singleton "hole" [aesonQQ| 2 |])-        match [qq| (unordered) [{foo: _hole}, ...] |] [aesonQQ| [{foo: 2}, 1] |] `shouldBe`-          pure (HashMap.singleton "hole" [aesonQQ| 2 |])-     -- https://github.com/supki/aeson-match-qq/issues/7     it "#7" $ do       match [qq| {foo: _} |] [aesonQQ| {} |] `shouldBe`@@ -147,25 +121,6 @@             { knownValues = [Object (Box {knownValues = [], extendable = False})]             , extendable = False             })--    -- https://github.com/supki/aeson-match-qq/issues/13-    it "#13" $-      [qq| "Слава Україні" |] `shouldMatch` [aesonQQ| "Слава Україні" |]--    -- https://github.com/supki/aeson-match-qq/issues/18-    it "#18" $ do-      -- string ~ string-      match [qq| "foo" |] [aesonQQ| "bar" |] `shouldBe`-        mismatch [] (String "foo") (Aeson.String "bar")-      -- string !~ number-      match [qq| "foo" |] [aesonQQ| 4 |] `shouldBe`-        mistype [] (String "foo") (Aeson.Number 4)-      -- string !~ null-      match [qq| "foo" |] [aesonQQ| null |] `shouldBe`-        mistype [] (String "foo") Aeson.Null-      -- null !~ number-      match [qq| null |] [aesonQQ| 4 |] `shouldBe`-        mistype [] Null (Aeson.Number 4)  newtype ToEncoding a = ToEncoding { unToEncoding :: a }     deriving (Show, Eq, Num)