content: {-# LANGUAGE QuasiQuotes #-}
  
  module Data.Aeson.Schema.Examples
    ( examples
    ) where
  
  import           Data.Aeson        (Value)
  import           Data.Aeson.LitQQ  (aesonLitQQ)
  import qualified Data.Map          as M
  import           Data.Text         (Text)
  
  import           Data.Aeson.Schema
  
  examples :: (String -> IO () -> a)
           -> (Graph Schema Text -> Schema Text -> Value -> IO ())
           -> (Graph Schema Text -> Schema Text -> Value -> IO ())
           -> [a]
  examples testCase assertValid' assertInvalid' =
    [ testCase "patternProperties" $ do
        let schema = [schemaQQ| {
          "type": "object",
          "properties": {
            "positiveNumber": {
              "type": "number",
              "minimum": 0,
              "exclusiveMinimum": true
            }
          },
          "patternProperties": {
            ".+Number$": { "type": "integer" },
            ".+String$": { "type": "string" }
          }
        } |]
        assertValid schema [aesonLitQQ| { "positiveNumber": 13 } |]
        assertInvalid schema [aesonLitQQ| { "positiveNumber": -13 } |]
        assertInvalid schema [aesonLitQQ| { "positiveNumber": 13.5 } |]
        assertValid schema [aesonLitQQ| { "fooString": "foo", "barString": "bar" } |]
        assertInvalid schema [aesonLitQQ| { "fooString": null, "barString": "bar" } |]
    , testCase "additionalProperties" $ do
        let additionalNumbers = [schemaQQ| {
          "type": "object",
          "properties": { "null": { "type": "null" } },
          "patternProperties": {
            ".+String$": { "type": "string" }
          },
          "additionalProperties": { "type": "number" }
        } |]
        assertValid additionalNumbers [aesonLitQQ| {
          "null": null,
          "emptyString": "",
          "oneMoreThing": 23,
          "theLastThing": 999
        } |]
        assertInvalid additionalNumbers [aesonLitQQ| { "null": null, "notANumber": true } |]
        let noAdditionalProperties = [schemaQQ| {
          "type": "object",
          "properties": { "null": { "type": "null" } },
          "patternProperties": {
            ".+String$": { "type": "string" }
          },
          "additionalProperties": false
        } |]
        assertValid noAdditionalProperties [aesonLitQQ| { "null": null, "emptyString": "" } |]
        assertInvalid noAdditionalProperties [aesonLitQQ| {
          "null": null,
          "emptyString": "",
          "oneMoreThing": 23,
          "theLastThing": 999
        } |]
    , testCase "disallow" $ do
        let onlyFloats = [schemaQQ| { "type": "number", "disallow": "integer" } |]
        assertInvalid onlyFloats [aesonLitQQ| 9 |]
        assertValid onlyFloats [aesonLitQQ| 9.75 |]
        let notLengthThree = [schemaQQ| {
          "type": "array",
          "disallow": [{
            "type": "array",
            "minItems": 3,
            "maxItems": 3
          }]
        } |]
        assertValid notLengthThree [aesonLitQQ| [] |]
        assertValid notLengthThree [aesonLitQQ| [1] |]
        assertValid notLengthThree [aesonLitQQ| [1, 2] |]
        assertInvalid notLengthThree [aesonLitQQ| [1, 2, 3] |]
        assertValid notLengthThree [aesonLitQQ| [1, 2, 3, 4] |]
        let everythingExceptNumbers = [schemaQQ| { "disallow": "number" } |]
        assertInvalid everythingExceptNumbers [aesonLitQQ| 3 |]
        assertInvalid everythingExceptNumbers [aesonLitQQ| 3.5 |]
        assertValid everythingExceptNumbers [aesonLitQQ| true |]
        assertValid everythingExceptNumbers [aesonLitQQ| "nobody expects the ..." |]
        assertValid everythingExceptNumbers [aesonLitQQ| { "eins": 1, "zwei": 2 } |]
        assertValid everythingExceptNumbers [aesonLitQQ| ["eins", "zwei"] |]
        assertValid everythingExceptNumbers [aesonLitQQ| null |]
    , testCase "type: subschema" $ do
        let schema = [schemaQQ| {
          "type": [
            {
              "type": "object",
              "properties": { "insert": { "type": "string", "minLength": 1 } },
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": { "delete": { "type": "number", "minimum": 1 } },
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": { "retain": { "type": "number", "minimum": 1 } },
              "additionalProperties": false
            }
          ]
        } |]
        assertValid schema [aesonLitQQ| { "insert": "lorem" } |]
        assertInvalid schema [aesonLitQQ| { "insert": "lorem", "delete": 5 } |]
        assertValid schema [aesonLitQQ| { "delete": 5 } |]
        assertInvalid schema [aesonLitQQ| { "delete": 5, "retain": 76 } |]
        assertValid schema [aesonLitQQ| { "retain": 76 } |]
    , testCase "dependencies" $ do
        let aRequiresB = [schemaQQ| {
          "type": "object",
          "dependencies": { "a": "b" }
        } |]
        assertValid aRequiresB [aesonLitQQ| {} |]
        assertValid aRequiresB [aesonLitQQ| { "b": false } |]
        assertValid aRequiresB [aesonLitQQ| { "a": true, "b": false } |]
        assertInvalid aRequiresB [aesonLitQQ| { "a": 3 } |]
        let aRequiresBToBeANumber = [schemaQQ| {
          "type": "object",
          "dependencies": { "a": { "properties": { "b": { "type": "number" } } } }
        } |]
        assertValid aRequiresBToBeANumber [aesonLitQQ| {} |]
        assertValid aRequiresBToBeANumber [aesonLitQQ| { "b": "lorem" } |]
        assertValid aRequiresBToBeANumber [aesonLitQQ| { "a": "yes, we can" } |]
        assertInvalid aRequiresBToBeANumber [aesonLitQQ| { "a": "yes, we can", "b": "lorem" } |]
        assertValid aRequiresBToBeANumber [aesonLitQQ| { "a": "hi there", "b": 42 } |]
        let aDisallowsB = [schemaQQ| {
          "type": "object",
          "dependencies": {
            "a": {
              "disallow": [{
                "properties": {
                  "b": { "type": "any", "required": true }
                }
              }]
            }
          }
        } |]
        assertValid aDisallowsB [aesonLitQQ| { "a": "lorem" } |]
        assertValid aDisallowsB [aesonLitQQ| { "b": 42 } |]
        assertInvalid aDisallowsB [aesonLitQQ| { "a": "lorem", "b": 42 } |]
    , testCase "extends" $ do
        let schema = [schemaQQ| {
          "type": "object",
          "properties": {
            "a": { "type": "number" }
          },
          "extends": [
            {
              "properties": {
                "a": { "required": true }
              }
            },
            {
              "patternProperties": {
                "^[a-z]$": { "minimum": -3 }
              }
            }
          ]
        } |]
        assertValid schema [aesonLitQQ| { "a": 2 } |]
        assertInvalid schema [aesonLitQQ| {} |]
        assertInvalid schema [aesonLitQQ| { "a": -4 } |]
        assertInvalid schema [aesonLitQQ| { "a": "foo" } |]
        assertInvalid schema [aesonLitQQ| { "a": -1, "b": -10 } |]
        assertValid schema [aesonLitQQ| { "a": -1, "ba": -10 } |]
    , testCase "$ref" $ do
        let
          a = [schemaQQ| { "$ref": "b", "minimum": 3 } |]
          b = [schemaQQ| { "type": "number", "maximum": 2 } |]
          graph = M.fromList [("a", a), ("b", b)]
        assertValid' graph a [aesonLitQQ| 1 |]
        assertInvalid' graph a [aesonLitQQ| 4 |]
    ]
    where
      assertValid = assertValid' M.empty
      assertInvalid = assertInvalid' M.empty
  