diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..870e5de --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/.* +!/.gitignore +!/.travis.yml +bower_components/ +node_modules/ +output/ +dist/ +npm-debug.log \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0d10c95 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +node_js: + - 0.10 +env: + - TAG=v0.7.0 +install: + - wget -O $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz + - sudo tar zxvf $HOME/purescript.tar.gz -C /usr/local/bin purescript/psc{,i,-docs,-bundle} --strip-components=1 + - sudo chmod a+x /usr/local/bin/psc{,i,-docs,-bundle} + - npm install bower gulp -g + - npm install && bower install +script: + - gulp test \ No newline at end of file diff --git a/README.md b/README.md index ece72ae..058e25f 100644 --- a/README.md +++ b/README.md @@ -1 +1,17 @@ +[![Build Status](https://travis-ci.org/purescript-contrib/purescript-argonaut-core.svg?branch=master)](https://travis-ci.org/purescript-contrib/purescript-argonaut-core) + # purescript-argonaut-core + +Core part of __purescript-argonaut__ contains basic types for `Json`, folds over them, tests, printer and parser + +## Installation + +```shell +bower install purescript-argonaut-core +``` + +## Documentation + +- [Data.Argonaut.Core](docs/Data/Argonaut/Core.md) +- [Data.Argonaut.Parser](docs/Data/Argonaut/Parser.md) +- [Data.Argonaut.Printer](docs/Data/Argonaut/Printer.md) diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..f826dda --- /dev/null +++ b/bower.json @@ -0,0 +1,29 @@ +{ + "name": "purescript-argonaut-core", + "homepage": "https://github.com/purescript-contrib/purescript-argonaut-core", + "authors": [ + "Maxim Zimaliev ", + "Hardy Jones <>", + "John A. De Goes " + ], + "description": "Core of purescript-argonaut library, it provides basic types, folds and combinators for `Json`", + "keywords": [ + "purescript", + "argonaut", + "json" + ], + "license": "MIT", + "dependencies": { + "purescript-prelude": "^0.1.0", + "purescript-functions": "^0.1.0", + "purescript-maybe": "^0.3.2", + "purescript-tuples": "^0.4.0", + "purescript-maps": "^0.4.0" + }, + "devDependencies": { + "purescript-eff": "^0.1.0", + "purescript-console": "^0.1.0", + "purescript-strongcheck": "^0.10.0", + "purescript-foldable-traversable": "^0.4.0" + } +} diff --git a/docs/Data/Argonaut/Core.md b/docs/Data/Argonaut/Core.md new file mode 100644 index 0000000..8c2d7cc --- /dev/null +++ b/docs/Data/Argonaut/Core.md @@ -0,0 +1,263 @@ +## Module Data.Argonaut.Core + +#### `JBoolean` + +``` purescript +type JBoolean = Boolean +``` + +#### `JNumber` + +``` purescript +type JNumber = Number +``` + +#### `JString` + +``` purescript +type JString = String +``` + +#### `JAssoc` + +``` purescript +type JAssoc = Tuple String Json +``` + +#### `JArray` + +``` purescript +type JArray = Array Json +``` + +#### `JObject` + +``` purescript +type JObject = StrMap Json +``` + +#### `JNull` + +``` purescript +data JNull :: * +``` + +##### Instances +``` purescript +instance eqJNull :: Eq JNull +instance ordJNull :: Ord JNull +instance showJNull :: Show JNull +``` + +#### `Json` + +``` purescript +data Json :: * +``` + +##### Instances +``` purescript +instance eqJson :: Eq Json +instance ordJson :: Ord Json +instance showJson :: Show Json +``` + +#### `foldJson` + +``` purescript +foldJson :: forall a. (JNull -> a) -> (JBoolean -> a) -> (JNumber -> a) -> (JString -> a) -> (JArray -> a) -> (JObject -> a) -> Json -> a +``` + +#### `foldJsonNull` + +``` purescript +foldJsonNull :: forall a. a -> (JNull -> a) -> Json -> a +``` + +#### `foldJsonBoolean` + +``` purescript +foldJsonBoolean :: forall a. a -> (JBoolean -> a) -> Json -> a +``` + +#### `foldJsonNumber` + +``` purescript +foldJsonNumber :: forall a. a -> (JNumber -> a) -> Json -> a +``` + +#### `foldJsonString` + +``` purescript +foldJsonString :: forall a. a -> (JString -> a) -> Json -> a +``` + +#### `foldJsonArray` + +``` purescript +foldJsonArray :: forall a. a -> (JArray -> a) -> Json -> a +``` + +#### `foldJsonObject` + +``` purescript +foldJsonObject :: forall a. a -> (JObject -> a) -> Json -> a +``` + +#### `isNull` + +``` purescript +isNull :: Json -> Boolean +``` + +#### `isBoolean` + +``` purescript +isBoolean :: Json -> Boolean +``` + +#### `isNumber` + +``` purescript +isNumber :: Json -> Boolean +``` + +#### `isString` + +``` purescript +isString :: Json -> Boolean +``` + +#### `isArray` + +``` purescript +isArray :: Json -> Boolean +``` + +#### `isObject` + +``` purescript +isObject :: Json -> Boolean +``` + +#### `toNull` + +``` purescript +toNull :: Json -> Maybe JNull +``` + +#### `toBoolean` + +``` purescript +toBoolean :: Json -> Maybe JBoolean +``` + +#### `toNumber` + +``` purescript +toNumber :: Json -> Maybe JNumber +``` + +#### `toString` + +``` purescript +toString :: Json -> Maybe JString +``` + +#### `toArray` + +``` purescript +toArray :: Json -> Maybe JArray +``` + +#### `toObject` + +``` purescript +toObject :: Json -> Maybe JObject +``` + +#### `fromNull` + +``` purescript +fromNull :: JNull -> Json +``` + +#### `fromBoolean` + +``` purescript +fromBoolean :: JBoolean -> Json +``` + +#### `fromNumber` + +``` purescript +fromNumber :: JNumber -> Json +``` + +#### `fromString` + +``` purescript +fromString :: JString -> Json +``` + +#### `fromArray` + +``` purescript +fromArray :: JArray -> Json +``` + +#### `fromObject` + +``` purescript +fromObject :: JObject -> Json +``` + +#### `jsonNull` + +``` purescript +jsonNull :: Json +``` + +#### `jsonTrue` + +``` purescript +jsonTrue :: Json +``` + +#### `jsonFalse` + +``` purescript +jsonFalse :: Json +``` + +#### `jsonZero` + +``` purescript +jsonZero :: Json +``` + +#### `jsonEmptyArray` + +``` purescript +jsonEmptyArray :: Json +``` + +#### `jsonEmptyObject` + +``` purescript +jsonEmptyObject :: Json +``` + +#### `jsonSingletonArray` + +``` purescript +jsonSingletonArray :: Json -> Json +``` + +#### `jsonSingletonObject` + +``` purescript +jsonSingletonObject :: String -> Json -> Json +``` + + diff --git a/docs/Data/Argonaut/Parser.md b/docs/Data/Argonaut/Parser.md new file mode 100644 index 0000000..90b9a13 --- /dev/null +++ b/docs/Data/Argonaut/Parser.md @@ -0,0 +1,9 @@ +## Module Data.Argonaut.Parser + +#### `jsonParser` + +``` purescript +jsonParser :: String -> Either String Json +``` + + diff --git a/docs/Data/Argonaut/Printer.md b/docs/Data/Argonaut/Printer.md new file mode 100644 index 0000000..6e21c92 --- /dev/null +++ b/docs/Data/Argonaut/Printer.md @@ -0,0 +1,15 @@ +## Module Data.Argonaut.Printer + +#### `Printer` + +``` purescript +class Printer a where + printJson :: Json -> a +``` + +##### Instances +``` purescript +instance printerString :: Printer String +``` + + diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..dea305b --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,70 @@ +'use strict' + + +var gulp = require('gulp') + , purescript = require('gulp-purescript') + , run = require('gulp-run') + , runSequence = require('run-sequence') + ; + +function sequence() { + var args = [].slice.apply(arguments); + return function() { + runSequence.apply(null, args); + }; +} + +var sources = [ + 'src/**/*.purs', + 'bower_components/purescript-*/src/**/*.purs' +]; + +var foreigns = [ + 'src/**/*.js', + 'bower_components/purescript-*/src/**/*.js' +]; + +var testSources = [ + 'test/**/*.purs' +]; + +var testForeigns = [ + 'test/**/*.js' +]; + +gulp.task('docs', function() { + return purescript.pscDocs({ + src: sources, + docgen: { + "Data.Argonaut.Core": "docs/Data/Argonaut/Core.md", + "Data.Argonaut.Printer": "docs/Data/Argonaut/Printer.md", + "Data.Argonaut.Parser": "docs/Data/Argonaut/Parser.md" + } + }); +}); + + +gulp.task('make', function() { + return purescript.psc({ + src: sources, + ffi: foreigns + }); +}); + +gulp.task('test-make', function() { + return purescript.psc({ + src: sources.concat(testSources), + ffi: foreigns.concat(testForeigns) + }); +}); + +gulp.task('test', ['test-make'], function() { + return purescript.pscBundle({ + src: "output/**/*.js", + main: "Test.Main", + output: "dist/test.js" + }).pipe(run('node dist/test.js')); +}); + + +gulp.task("default", sequence("make", "docs")); diff --git a/package.json b/package.json new file mode 100644 index 0000000..71fb117 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "purescript-argonaut-core", + "description": "Core of purescript-argonaut library, it provides basic types, folds and combinators for `Json`", + "license": "MIT", + "dependencies": { + "gulp": "^3.9.0", + "gulp-purescript": "^0.5.0", + "gulp-run": "^1.6.8", + "run-sequence": "^1.1.1" + } +} diff --git a/src/Data/Argonaut/Core.js b/src/Data/Argonaut/Core.js new file mode 100644 index 0000000..079a835 --- /dev/null +++ b/src/Data/Argonaut/Core.js @@ -0,0 +1,120 @@ +// module Data.Argonaut.Core + +function id(x) { + return x; +} + + +exports.fromNull = function() { + return null; +}; + +exports.fromBoolean = id; +exports.fromNumber = id; +exports.fromString = id; +exports.fromArray = id; +exports.fromObject = id; + +exports.jsonNull = null; + +exports._stringify = function(j) { + return JSON.stringify(j); +}; + +exports._foldJson = function(isNull, isBool, isNum, isStr, isArr, isObj, j) { + if (j == null) return isNull(null); + else if (typeof j === 'boolean') return isBool(j); + else if (typeof j === 'number') return isNum(j); + else if (typeof j === 'string') return isStr(j); + else if (Object.prototype.toString.call(j) === '[object Array]') + return isArr(j); + else return isObj(j); +}; + +function _compare(EQ, GT, LT, a, b) { + function isArray(a) { + return Object.prototype.toString.call(a) === '[object Array]'; + } + function keys(o) { + var a = []; + for (var k in o) { + a.push(k); + } + return a; + } + + if (a == null) { + if (b == null) return EQ; + else return LT; + } else if (typeof a === 'boolean') { + if (typeof b === 'boolean') { + // boolean / boolean + if (a === b) return EQ; + else if (a == false) return LT; + else return GT; + } else if (b == null) return GT; + else return LT; + } else if (typeof a === 'number') { + if (typeof b === 'number') { + if (a === b) return EQ; + else if (a < b) return LT; + else return GT; + } else if (b == null) return GT; + else if (typeof b === 'boolean') return GT; + else return LT; + } else if (typeof a === 'string') { + if (typeof b === 'string') { + if (a === b) return EQ; + else if (a < b) return LT; + else return GT; + } else if (b == null) return GT; + else if (typeof b === 'boolean') return GT; + else if (typeof b === 'number') return GT; + else return LT; + } else if (isArray(a)) { + if (isArray(b)) { + for (var i = 0; i < Math.min(a.length, b.length); i++) { + var c = _compare(EQ, GT, LT, a[i], b[i]); + + if (c !== EQ) return c; + } + if (a.length === b.length) return EQ; + else if (a.length < b.length) return LT; + else return GT; + } else if (b == null) return GT; + else if (typeof b === 'boolean') return GT; + else if (typeof b === 'number') return GT; + else if (typeof b === 'string') return GT; + else return LT; + } + else { + if (b == null) return GT; + else if (typeof b === 'boolean') return GT; + else if (typeof b === 'number') return GT; + else if (typeof b === 'string') return GT; + else if (isArray(b)) return GT; + else { + var akeys = keys(a); + var bkeys = keys(b); + + var keys = akeys.concat(bkeys).sort(); + + for (var i = 0; i < keys.length; i++) { + var k = keys[i]; + + if (a[k] === undefined) return LT; + else if (b[k] === undefined) return GT; + + var c = _compare(EQ, GT, LT, a[k], b[k]); + + if (c !== EQ) return c; + } + + if (akeys.length === bkeys.length) return EQ; + else if (akeys.length < bkeys.length) return LT; + else return GT; + } + } +}; + +exports._compare = _compare; diff --git a/src/Data/Argonaut/Core.purs b/src/Data/Argonaut/Core.purs new file mode 100644 index 0000000..c7c871a --- /dev/null +++ b/src/Data/Argonaut/Core.purs @@ -0,0 +1,200 @@ +module Data.Argonaut.Core + ( Json(..) + , JNull(..) + , JBoolean(..) + , JNumber(..) + , JString(..) + , JAssoc(..) + , JArray(..) + , JObject(..) + , foldJson + , foldJsonNull + , foldJsonBoolean + , foldJsonNumber + , foldJsonString + , foldJsonArray + , foldJsonObject + , isNull + , isBoolean + , isNumber + , isString + , isArray + , isObject + , fromNull + , fromBoolean + , fromNumber + , fromString + , fromArray + , fromObject + , toNull + , toBoolean + , toNumber + , toString + , toArray + , toObject + , jsonNull + , jsonFalse + , jsonTrue + , jsonZero + , jsonEmptyArray + , jsonSingletonArray + , jsonEmptyObject + , jsonSingletonObject + ) where + +import Prelude + +import Data.Tuple (Tuple(..)) +import Data.Maybe (Maybe(..)) +import Data.Function + +import qualified Data.StrMap as M + +type JBoolean = Boolean +type JNumber = Number +type JString = String +type JAssoc = Tuple String Json +type JArray = Array Json +type JObject = M.StrMap Json + +foreign import data JNull :: * +foreign import data Json :: * + +foldJson :: forall a. + (JNull -> a) -> (JBoolean -> a) -> (JNumber -> a) -> + (JString -> a) -> (JArray -> a) -> (JObject -> a) -> + Json -> a +foldJson a b c d e f json = runFn7 _foldJson a b c d e f json + +foldJsonNull :: forall a. a -> (JNull -> a) -> Json -> a +foldJsonNull d f j = runFn7 _foldJson f (const d) (const d) (const d) (const d) (const d) j + +foldJsonBoolean :: forall a. a -> (JBoolean -> a) -> Json -> a +foldJsonBoolean d f j = runFn7 _foldJson (const d) f (const d) (const d) (const d) (const d) j + +foldJsonNumber :: forall a. a -> (JNumber -> a) -> Json -> a +foldJsonNumber d f j = runFn7 _foldJson (const d) (const d) f (const d) (const d) (const d) j + +foldJsonString :: forall a. a -> (JString -> a) -> Json -> a +foldJsonString d f j = runFn7 _foldJson (const d) (const d) (const d) f (const d) (const d) j + +foldJsonArray :: forall a. a -> (JArray -> a) -> Json -> a +foldJsonArray d f j = runFn7 _foldJson (const d) (const d) (const d) (const d) f (const d) j + +foldJsonObject :: forall a. a -> (JObject -> a) -> Json -> a +foldJsonObject d f j = runFn7 _foldJson (const d) (const d) (const d) (const d) (const d) f j + +verbJsonType :: forall a b. b -> (a -> b) -> (b -> (a -> b) -> Json -> b) -> Json -> b +verbJsonType def f fold = fold def f + + +-- Tests +isJsonType :: forall a. (Boolean -> (a -> Boolean) -> Json -> Boolean) -> + Json -> Boolean +isJsonType = verbJsonType false (const true) + +isNull :: Json -> Boolean +isNull = isJsonType foldJsonNull + +isBoolean :: Json -> Boolean +isBoolean = isJsonType foldJsonBoolean + +isNumber :: Json -> Boolean +isNumber = isJsonType foldJsonNumber + +isString :: Json -> Boolean +isString = isJsonType foldJsonString + +isArray :: Json -> Boolean +isArray = isJsonType foldJsonArray + +isObject :: Json -> Boolean +isObject = isJsonType foldJsonObject + +-- Decoding + +toJsonType :: forall a b. (Maybe a -> (a -> Maybe a) -> Json -> Maybe a) -> + Json -> Maybe a +toJsonType = verbJsonType Nothing Just + +toNull :: Json -> Maybe JNull +toNull = toJsonType foldJsonNull + +toBoolean :: Json -> Maybe JBoolean +toBoolean = toJsonType foldJsonBoolean + +toNumber :: Json -> Maybe JNumber +toNumber = toJsonType foldJsonNumber + +toString :: Json -> Maybe JString +toString = toJsonType foldJsonString + +toArray :: Json -> Maybe JArray +toArray = toJsonType foldJsonArray + +toObject :: Json -> Maybe JObject +toObject = toJsonType foldJsonObject + +-- Encoding + +foreign import fromNull :: JNull -> Json +foreign import fromBoolean :: JBoolean -> Json +foreign import fromNumber :: JNumber -> Json +foreign import fromString :: JString -> Json +foreign import fromArray :: JArray -> Json +foreign import fromObject :: JObject -> Json + +-- Defaults + +foreign import jsonNull :: Json + +jsonTrue :: Json +jsonTrue = fromBoolean true +jsonFalse :: Json +jsonFalse = fromBoolean false + +jsonZero :: Json +jsonZero = fromNumber 0.0 + +jsonEmptyString :: Json +jsonEmptyString = fromString "" + +jsonEmptyArray :: Json +jsonEmptyArray = fromArray [] + +jsonEmptyObject :: Json +jsonEmptyObject = fromObject M.empty + +jsonSingletonArray :: Json -> Json +jsonSingletonArray j = fromArray [j] + +jsonSingletonObject :: String -> Json -> Json +jsonSingletonObject key val = fromObject $ M.singleton key val + +-- Instances + +instance eqJNull :: Eq JNull where + eq _ _ = true + +instance ordJNull :: Ord JNull where + compare _ _ = EQ + +instance showJNull :: Show JNull where + show _ = "null" + +instance eqJson :: Eq Json where + eq j1 j2 = (compare j1 j2) == EQ + +instance ordJson :: Ord Json where + compare a b = runFn5 _compare EQ GT LT a b + +instance showJson :: Show Json where + show = _stringify + +-- Foreigns + +foreign import _stringify :: Json -> String +foreign import _foldJson :: forall z. Fn7 (JNull -> z) (JBoolean -> z) + (JNumber -> z) (JString -> z) (JArray -> z) + (JObject -> z) Json z +foreign import _compare :: Fn5 Ordering Ordering Ordering Json Json Ordering diff --git a/src/Data/Argonaut/Parser.js b/src/Data/Argonaut/Parser.js new file mode 100644 index 0000000..e48d568 --- /dev/null +++ b/src/Data/Argonaut/Parser.js @@ -0,0 +1,10 @@ +// module Data.Argonaut.Parser + +exports._jsonParser = function(fail, succ, s) { + try { + return succ(JSON.parse(s)); + } + catch(e) { + return fail(e.message); + } +}; diff --git a/src/Data/Argonaut/Parser.purs b/src/Data/Argonaut/Parser.purs new file mode 100644 index 0000000..bbc6cd3 --- /dev/null +++ b/src/Data/Argonaut/Parser.purs @@ -0,0 +1,10 @@ +module Data.Argonaut.Parser (jsonParser) where + +import Data.Argonaut.Core (Json()) +import Data.Function (Fn3(), runFn3) +import Data.Either (Either(..)) + +foreign import _jsonParser :: forall a. Fn3 (String -> a) (Json -> a) String a + +jsonParser :: String -> Either String Json +jsonParser j = runFn3 _jsonParser Left Right j diff --git a/src/Data/Argonaut/Printer.purs b/src/Data/Argonaut/Printer.purs new file mode 100644 index 0000000..3fbba3f --- /dev/null +++ b/src/Data/Argonaut/Printer.purs @@ -0,0 +1,11 @@ +module Data.Argonaut.Printer (Printer, printJson) where + +import Prelude +import Data.Argonaut.Core (Json()) + +class Printer a where + printJson :: Json -> a + +instance printerString :: Printer String where + printJson = show + diff --git a/test/Test/Main.js b/test/Test/Main.js new file mode 100644 index 0000000..8f17505 --- /dev/null +++ b/test/Test/Main.js @@ -0,0 +1,11 @@ +// module Test.Main + +exports.thisIsNull = null; +exports.thisIsBoolean = true; +exports.thisIsNumber = 12; +exports.thisIsString = "foobar"; +exports.thisIsArray = ["foo", "bar"]; +exports.thisIsObject = { + foo: "bar" +}; +exports.nil = null; diff --git a/test/Test/Main.purs b/test/Test/Main.purs new file mode 100644 index 0000000..2179da4 --- /dev/null +++ b/test/Test/Main.purs @@ -0,0 +1,151 @@ +module Test.Main where + +import Prelude + +import Control.Monad.Eff (Eff()) +import Control.Monad.Eff.Console (log) +import Data.Maybe (Maybe(..)) +import Data.Maybe.Unsafe (fromJust) +import Data.Either (isLeft, isRight, Either(..)) +import Data.Tuple (Tuple(..)) +import Data.Foldable (for_) +import qualified Data.Array as A +import qualified Data.StrMap as M + +import Data.Argonaut.Core +import Data.Argonaut.Parser +import Data.Argonaut.Printer + +import Test.StrongCheck (assert, (), quickCheck, Result()) + +foreign import thisIsNull :: Json +foreign import thisIsBoolean :: Json +foreign import thisIsNumber :: Json +foreign import thisIsString :: Json +foreign import thisIsArray :: Json +foreign import thisIsObject :: Json +foreign import nil :: JNull + +isTest :: Eff _ Unit +isTest = do + assert (isNull thisIsNull "Error in null test") + assert (isBoolean thisIsBoolean "Error in boolean test") + assert (isNumber thisIsNumber "Error in number test") + assert (isString thisIsString "Error in string test") + assert (isArray thisIsArray "Error in array test") + assert (isObject thisIsObject "Error in object test") + +foldTest :: Eff _ Unit +foldTest = do + assert (foldFn thisIsNull == "null" "Error in foldJson null") + assert (foldFn thisIsBoolean == "boolean" "Error in foldJson boolean") + assert (foldFn thisIsNumber == "number" "Error in foldJson number") + assert (foldFn thisIsString == "string" "Error in foldJson string") + assert (foldFn thisIsArray == "array" "Error in foldJson array") + assert (foldFn thisIsObject == "object" "Error in foldJson object") + + +foldFn :: Json -> String +foldFn = foldJson + (const "null") + (const "boolean") + (const "number") + (const "string") + (const "array") + (const "object") + +cases :: Array Json +cases = + [ thisIsNull + , thisIsBoolean + , thisIsNumber + , thisIsString + , thisIsArray + , thisIsObject + ] + +foldXXX :: Eff _ Unit +foldXXX = do + assert ((foldJsonNull "not null" (const "null") <$> cases) == + ["null", "not null", "not null", "not null", "not null", "not null"] + "Error in foldJsonNull") + assert ((foldJsonBoolean "not boolean" (const "boolean") <$> cases) == + ["not boolean", "boolean", "not boolean", "not boolean", "not boolean", "not boolean"] + "Error in foldJsonBoolean") + assert ((foldJsonNumber "not number" (const "number") <$> cases) == + ["not number", "not number", "number", "not number", "not number", "not number"] + "Error in foldJsonNumber") + + assert ((foldJsonString "not string" (const "string") <$> cases) == + ["not string", "not string", "not string", "string", "not string", "not string"] + "Error in foldJsonString") + + assert ((foldJsonArray "not array" (const "array") <$> cases) == + ["not array", "not array", "not array", "not array", "array", "not array"] + "Error in foldJsonArray") + assert ((foldJsonObject "not object" (const "object") <$> cases) == + ["not object", "not object", "not object", "not object", "not object", "object"] + "Error in foldJsonObject") + + +fromTest :: Eff _ Unit +fromTest = do + assert ((foldJsonNull false (const true) (fromNull nil)) "Error in fromNull") + quickCheck (\bool -> foldJsonBoolean Nothing Just (fromBoolean bool) == Just bool "Error in fromBoolean") + quickCheck (\num -> foldJsonNumber Nothing Just (fromNumber num) == Just num "Error in fromNumber") + quickCheck (\str -> foldJsonString Nothing Just (fromString str) == Just str "Error in fromString") + quickCheck (\num -> + let arr :: Array Json + arr = A.singleton (fromNumber num) + in (foldJsonArray Nothing Just (fromArray arr) == Just arr) + "Error in fromArray") + quickCheck (\(Tuple str num) -> + let sm :: M.StrMap Json + sm = M.singleton str (fromNumber num) + in (foldJsonObject Nothing Just (fromObject sm) == Just sm) + "Error in fromObject") + +toTest :: Eff _ Unit +toTest = do + assert (assertion toNull thisIsNull "Error in toNull") + assert (assertion toBoolean thisIsBoolean "Error in toBoolean") + assert (assertion toNumber thisIsNumber "Error in toNumber") + assert (assertion toString thisIsString "Error in toString") + assert (assertion toArray thisIsArray "Error in toArray") + assert (assertion toObject thisIsObject "Error in toObject") + where + assertion :: forall a. (Eq a) => (Json -> Maybe a) -> Json -> String -> Result + assertion fn j msg = + let forCases = A.catMaybes (fn <$> cases) + exact = A.singleton $ fromJust $ fn j + in forCases == exact msg + + +parserTest :: Eff _ Unit +parserTest = do + assert ((isRight (jsonParser "{\"foo\": 1}")) "Error in jsonParser") + assert ((isLeft (jsonParser "\\\ffff")) "Error in jsonParser") + +printJsonTest :: Eff _ Unit +printJsonTest = do + for_ cases (assert <<< assertion) + where + assertion :: Json -> Result + assertion j = ((jsonParser (printJson j)) == Right j) "Error in printJson" + +main :: Eff _ Unit +main = do + log "isXxx tests" + isTest + log "foldJson tests" + foldTest + log "foldJsonXxx tests" + foldXXX + log "fromXxx tests" + fromTest + log "toXxx tests" + toTest + log "jsonParser tests" + parserTest + log "printJson tests" + printJsonTest