├── .envrc ├── .git-blame-ignore-revs ├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── .jvmopts │ ├── ci.yml │ └── scala-steward.yml ├── .gitignore ├── .scalafmt.conf ├── CONTRIBUTORS ├── COPYING ├── LICENCE ├── README.md ├── argonaut-benchmark └── src │ └── main │ ├── resources │ └── json-data │ │ ├── README │ │ ├── apache_builds.json │ │ ├── example.json │ │ ├── integers.json │ │ ├── jp10.json │ │ ├── jp100.json │ │ ├── jp50.json │ │ ├── largestring.json │ │ ├── manystrings.json │ │ ├── numbers.json │ │ ├── twitter1.json │ │ ├── twitter10.json │ │ ├── twitter100.json │ │ ├── twitter20.json │ │ └── twitter50.json │ └── scala │ └── argonaut │ └── benchmark │ ├── Benchmark.scala │ ├── CaliperBenchmark.scala │ ├── Data.scala │ ├── Main.scala │ └── ParserBench.scala ├── argonaut-cats └── shared │ └── src │ ├── main │ └── scala │ │ └── argonaut │ │ ├── ACursorCats.scala │ │ ├── ArgonautCats.scala │ │ ├── CodecJsonCats.scala │ │ ├── ContextCats.scala │ │ ├── CursorCats.scala │ │ ├── CursorHistoryCats.scala │ │ ├── CursorOpCats.scala │ │ ├── CursorOpElementCats.scala │ │ ├── DecodeJsonCats.scala │ │ ├── DecodeResultCats.scala │ │ ├── EncodeJsonCats.scala │ │ ├── HCursorCats.scala │ │ ├── JsonCats.scala │ │ ├── JsonIdentityCats.scala │ │ ├── JsonNumberCats.scala │ │ ├── JsonObjectCats.scala │ │ ├── PrettyParamsCats.scala │ │ └── StringWrapCats.scala │ └── test │ └── scala │ ├── argonaut │ ├── CursorHistoryCatsSpecification.scala │ ├── DecodeResultCatsSpecification.scala │ ├── EncodeJsonCatsSpecification.scala │ ├── JsonObjectCatsSpecification.scala │ └── arbitrary.scala │ └── org │ └── typelevel │ └── discipline │ └── specs2 │ └── Discipline.scala ├── argonaut-jawn └── shared │ └── src │ ├── main │ └── scala │ │ └── argonaut │ │ └── JawnParser.scala │ └── test │ └── scala │ └── argonaut │ └── JawnParserSpecification.scala ├── argonaut-monocle └── shared │ └── src │ ├── main │ └── scala │ │ └── argonaut │ │ ├── ACursorMonocle.scala │ │ ├── ArgonautMonocle.scala │ │ ├── CursorHistoryMonocle.scala │ │ ├── CursorOpMonocle.scala │ │ ├── DecodeResultMonocle.scala │ │ ├── HCursorMonocle.scala │ │ ├── JsonMonocle.scala │ │ ├── JsonNumberMonocle.scala │ │ ├── JsonObjectMonocle.scala │ │ ├── JsonPath.scala │ │ └── PrettyParamsMonocle.scala │ └── test │ └── scala │ └── argonaut │ ├── JsonMonocleSpecification.scala │ ├── JsonPathSpecification.scala │ └── PrettyParamsMonocleSpecification.scala ├── argonaut-scalaz └── shared │ └── src │ ├── main │ └── scala │ │ └── argonaut │ │ ├── ACursorScalaz.scala │ │ ├── ArgonautScalaz.scala │ │ ├── CodecJsonScalaz.scala │ │ ├── ContextScalaz.scala │ │ ├── CursorHistoryScalaz.scala │ │ ├── CursorOpElementScalaz.scala │ │ ├── CursorOpScalaz.scala │ │ ├── CursorScalaz.scala │ │ ├── DecodeJsonScalaz.scala │ │ ├── DecodeResultScalaz.scala │ │ ├── EncodeJsonScalaz.scala │ │ ├── HCursorScalaz.scala │ │ ├── JsonIdentityScalaz.scala │ │ ├── JsonNumberScalaz.scala │ │ ├── JsonObjectScalaz.scala │ │ ├── JsonScalaz.scala │ │ ├── PrettyParamsScalaz.scala │ │ └── StringWrapScalaz.scala │ └── test │ └── scala │ └── argonaut │ ├── JsonObjectScalazSpecification.scala │ └── JsonScalazSpecification.scala ├── argonaut ├── js-native │ └── src │ │ └── test │ │ ├── scala-2 │ │ └── argonaut │ │ │ └── ArgonautSpec.scala │ │ └── scala-3 │ │ └── argonaut │ │ └── ArgonautSpec.scala ├── js │ └── src │ │ └── test │ │ └── scala │ │ └── argonaut │ │ └── JsonFilesSpecification.scala ├── jvm │ └── src │ │ └── test │ │ ├── resources │ │ └── data │ │ │ ├── apache_builds.json │ │ │ ├── browser-gumf.index.json │ │ │ ├── browser-gumf.manifest.json │ │ │ ├── browser-gumf.messages0.json │ │ │ ├── browser-gumf.messages1.json │ │ │ ├── browser-gumf.messages2.json │ │ │ ├── browser-gumf.messages3.json │ │ │ ├── browser-gumf.palette-db.json │ │ │ ├── browser-gumf.search.json │ │ │ ├── browser-gumf.webapps.json │ │ │ ├── example.json │ │ │ ├── github.events.json │ │ │ ├── integers.json │ │ │ ├── jp10.json │ │ │ ├── jp100.json │ │ │ ├── jp50.json │ │ │ ├── numbers.json │ │ │ ├── travis.argonaut.json │ │ │ ├── travis.builds.json │ │ │ ├── travis.stats.json │ │ │ ├── twitter.learnkanji-200.json │ │ │ ├── twitter.plthulk-100.json │ │ │ ├── twitter.twitterapi-200.json │ │ │ ├── twitter1.json │ │ │ ├── twitter10.json │ │ │ ├── twitter100.json │ │ │ ├── twitter20.json │ │ │ └── twitter50.json │ │ └── scala │ │ └── argonaut │ │ ├── ArgonautSpec.scala │ │ ├── JsonFilesSpecification.scala │ │ └── PrettyParamsJVMSpecification.scala └── shared │ └── src │ ├── main │ ├── scala-2 │ │ └── argonaut │ │ │ ├── CodecJsonMacro.scala │ │ │ ├── DecodeJsonMacro.scala │ │ │ ├── EncodeJsonMacro.scala │ │ │ └── internal │ │ │ ├── Macros.scala │ │ │ └── MacrosCompat.scala │ ├── scala-3 │ │ └── argonaut │ │ │ ├── CodecJsonMacro.scala │ │ │ ├── DecodeJsonMacro.scala │ │ │ ├── EncodeJsonMacro.scala │ │ │ └── internal │ │ │ └── Macros.scala │ └── scala │ │ └── argonaut │ │ ├── ACursor.scala │ │ ├── Argonaut.scala │ │ ├── CodecJson.scala │ │ ├── Context.scala │ │ ├── Cursor.scala │ │ ├── CursorHistory.scala │ │ ├── CursorOp.scala │ │ ├── CursorOpElement.scala │ │ ├── DecodeJson.scala │ │ ├── DecodeResult.scala │ │ ├── EncodeJson.scala │ │ ├── EncodeJsonKey.scala │ │ ├── HCursor.scala │ │ ├── Json.scala │ │ ├── JsonIdentity.scala │ │ ├── JsonNumber.scala │ │ ├── JsonObject.scala │ │ ├── JsonParser.scala │ │ ├── Parse.scala │ │ ├── ParseWrap.scala │ │ ├── PrettyParams.scala │ │ └── StringWrap.scala │ └── test │ ├── scala-2 │ └── argonaut │ │ └── TestCompat.scala │ ├── scala-3 │ └── argonaut │ │ ├── CodecSpecificationScala3.scala │ │ └── TestCompat.scala │ └── scala │ └── argonaut │ ├── ACursorSpecification.scala │ ├── CodecNumberSpecification.scala │ ├── CodecOptionSpecification.scala │ ├── CodecSpecification.scala │ ├── CursorSpecification.scala │ ├── Data.scala │ ├── DecodeJsonSpecification.scala │ ├── EncodeJsonSpecification.scala │ ├── JsonFilesSpecBase.scala │ ├── JsonNumberSpecification.scala │ ├── JsonObjectSpecification.scala │ ├── JsonParserSpecification.scala │ ├── JsonSpecification.scala │ ├── KnownResults.scala │ ├── OptionParserSpecification.scala │ ├── PrettyParamsSpecification.scala │ ├── StringWrapSpecification.scala │ ├── TestTypes.scala │ └── example │ ├── CodecExample.scala │ ├── CursorExample.scala │ ├── JsonExample.scala │ ├── PolymorphicCursorExample.scala │ ├── PolymorphicHibridExample.scala │ └── PrettyParamsExample.scala ├── build.sbt ├── notes ├── 6.0-M2.markdown ├── 6.0-M3.markdown ├── 6.0-M4.markdown ├── 6.0-M5.markdown ├── 6.0-M6.markdown ├── 6.0-M7.markdown ├── 6.0-RC1.markdown ├── 6.0-RC2.markdown ├── 6.0-RC3.markdown ├── 6.0.1.markdown ├── 6.0.2.markdown ├── 6.0.3.markdown ├── 6.0.4.markdown ├── 6.0.markdown ├── 6.1-M1.markdown ├── 6.1-M2.markdown ├── 6.1-M3.markdown ├── 6.1-M4.markdown ├── 6.1-M5.markdown ├── 6.1-M6.markdown ├── 6.1.markdown ├── 6.2-M1.markdown └── about.markdown ├── project ├── Boilerplate.scala ├── InfoSettings.scala ├── PublishSettings.scala ├── ReplSettings.scala ├── ScalaSettings.scala ├── build.properties ├── build.scala └── plugins.sbt ├── sbt ├── shell.nix └── version.sbt /.envrc: -------------------------------------------------------------------------------- 1 | use nix -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Scala Steward: Reformat with scalafmt 3.8.1 2 | 35aaff517a391636191d045d949cf2dd19d4f263 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @xuwei-k 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | target-branch: "series/6.2.x" 12 | commit-message: 13 | prefix: "[6.2.x] " 14 | -------------------------------------------------------------------------------- /.github/workflows/.jvmopts: -------------------------------------------------------------------------------- 1 | -Xms2048M 2 | -Xmx4096M 3 | -Xss6M 4 | -XX:+UseG1GC 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | push: 5 | schedule: 6 | - cron: '0 0 * * 0' 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | timeout-minutes: 50 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | include: 15 | - java: 8 16 | - java: 25 17 | steps: 18 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 19 | - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 20 | with: 21 | java-version: ${{matrix.java}} 22 | distribution: zulu 23 | - uses: coursier/cache-action@4e2615869d13561d626ed48655e1a39e5b192b3c # v6.4.7 24 | - run: ./sbt -v 25 | -jvm-opts .github/workflows/.jvmopts 26 | scalafmtSbtCheck 27 | "+scalafmtCheckAll" 28 | "+mimaReportBinaryIssues" 29 | "+Test/compile" 30 | "+jvmParent/test" 31 | "+nativeParent/test" 32 | "project jsParent" 33 | testSequentialCross 34 | latest: 35 | runs-on: ubuntu-latest 36 | timeout-minutes: 30 37 | steps: 38 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 39 | - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 40 | with: 41 | java-version: 21 42 | distribution: temurin 43 | - uses: coursier/cache-action@4e2615869d13561d626ed48655e1a39e5b192b3c # v6.4.7 44 | - run: ./sbt -v 45 | -jvm-opts .github/workflows/.jvmopts 46 | "++ 3.7.1!" 47 | scalafmtCheckAll 48 | Test/compile 49 | jvmParent/test 50 | nativeParent/test 51 | "project jsParent" 52 | testSequential 53 | wasm: 54 | runs-on: ubuntu-latest 55 | timeout-minutes: 30 56 | steps: 57 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 58 | - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 59 | with: 60 | java-version: 21 61 | distribution: temurin 62 | - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 63 | with: 64 | node-version: 22 65 | - uses: coursier/cache-action@4e2615869d13561d626ed48655e1a39e5b192b3c # v6.4.7 66 | - run: ./sbt -v 67 | -jvm-opts .github/workflows/.jvmopts 68 | -Dscala_js_wasm 69 | "project jsParent" 70 | testSequential 71 | -------------------------------------------------------------------------------- /.github/workflows/scala-steward.yml: -------------------------------------------------------------------------------- 1 | name: scala-steward 2 | on: 3 | push: 4 | branches: [ master ] 5 | schedule: 6 | - cron: '0 0 * * *' 7 | workflow_dispatch: 8 | jobs: 9 | scala-steward: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.repository_owner == 'argonaut-io' }} 12 | steps: 13 | - name: Generate token 14 | id: generate_token 15 | uses: tibdex/github-app-token@v2 16 | with: 17 | app_id: 89628 18 | private_key: ${{ secrets.ARGONAUT_BOT_KEY }} 19 | - name: Launch Scala Steward 20 | uses: scala-steward-org/scala-steward-action@v2.77.0 21 | with: 22 | github-token: ${{ steps.generate_token.outputs.token }} 23 | author-email: "74832392+argonaut-bot[bot]@users.noreply.github.com" 24 | author-name: argonaut-bot[bot] 25 | branches: "master,series/6.2.x" 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | gen 3 | *.iws 4 | out 5 | target 6 | .idea 7 | .idea_modules 8 | .lib 9 | /lib 10 | /dependency 11 | /dist 12 | /tmp 13 | /project/boot 14 | *.swp 15 | repl-port 16 | *~ 17 | tags 18 | *.sublime-* 19 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "3.9.4" 2 | runner.dialect = Scala213Source3 3 | maxColumn = 120 4 | align.preset = none 5 | align.tokens = [] 6 | rewrite.rules = [ExpandImportSelectors, PreferCurlyFors] 7 | rewrite.imports.contiguousGroups = "no" 8 | rewrite.imports.groups = [[".*"]] 9 | continuationIndent.callSite = 2 10 | continuationIndent.defnSite = 2 11 | docstrings.style = keep 12 | includeCurlyBraceInSelectChains = false 13 | optIn.breakChainOnFirstMethodDot = false 14 | trailingCommas = preserve 15 | newlines.topLevelStatementBlankLines = [ 16 | { 17 | blanks { after = 1 } 18 | maxNest = 1 19 | regex = "Import" 20 | } 21 | ] 22 | project.layout = StandardConvention 23 | rewrite.scala3.convertToNewSyntax = true 24 | rewrite.scala3.newSyntax.control = false 25 | runner.dialectOverride.allowSignificantIndentation = false 26 | runner.dialectOverride.allowUnderscoreAsTypePlaceholder = false 27 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Tony Morris 2 | tmorris@tmorris.net 3 | http://tmorris.net/ 4 | -----BEGIN PGP PUBLIC KEY BLOCK----- 5 | Version: GnuPG v1.4.6 (GNU/Linux) 6 | 7 | mQGiBETNyC0RBAC3MYSZSbDZhBLKra2YUphB9OO6+qMFl/v2Lq8590ZfeE2WjIOu 8 | c/KGKyOigXztMrA4+iekUjM4FA8E6AlBRQiAqZK8HF0ftX5hDpuSyEKkZe3jcxxI 9 | BbhwX/SWHtDEVzwNmvO1wEnwhRE1oY/BCmy+bQ9wmAjlNav4UbOIcXcJUwCgz6FF 10 | UwDvPxzybgd9FNS14BE37n8D/AzGmGjunBW/x+g/ndwu6WEpTEn1ZNdja6VrNXSG 11 | xQ8XM+NBulroAIYX+YWdHsKnuGvSKCgVoc1ifVHdztA9sksID5GBGzhmVbIP2E16 12 | w/LEzqqwruv9dX0Y1b7n8hcnvbl4DgEgLgeQ+VgJfLUkY2jFZ2m3dEBRH9USSgB/ 13 | V2pBBACP4Us2ZBYEXtMG29g6GqyeeJLEP34PLYVHWJZbet/wPQsHFhYahhzjwZGG 14 | Srp0JUpegJOdaqX/Y0nio2whgCpqJcrbGuUBqNgQI3k4gvBwnPyx7jM0kfHfFORG 15 | lq9SDbfLpV0EJx6fWXGStW33zsQQvenDcV2F5czzKQcy66BFwbQhVG9ueSBNb3Jy 16 | aXMgPHRtb3JyaXNAdG1vcnJpcy5uZXQ+iGMEExECACMFAkTNyC0FCQlmAYAGCwkI 17 | BwMCBBUCCAMEFgIDAQIeAQIXgAAKCRCaemCth7qvrf0pAKCFii2pI2W1BKVFuQcw 18 | yoNxP0CAUACgyhuI9isCvtrOkeyjDmCVueRCdaC5Ag0ERM3IThAIAJ6A1z+d40ve 19 | WvPIhpFiGfoS8UX4YdgYpo2mC/orY0xszBitogtaTHQHU5YDemGg81plNg9I3DbM 20 | Er8uyONV4DqF6RbLj4w+iA6zn93+PTEZ73ydhxF6vDuojpVZPXVzXzpgyXHkEVLC 21 | 3hKL9oVlEsh+DWCvCiSAIy780JZ3FNVuMC3VH4qKxTw0CwPuuZvVfnMoIRfpODRR 22 | fVEk2VDor+lr8kqJkBaHgN5o/AvOXC7QCYadwbEkpr0ecxIZ1VcASYytIIM3YNL7 23 | ZcHWwU5PCNLOdMXPqOdthhDhsHkKJNEXXr0YsjX/bQqYOUqYKPDyqh/yrrRO9Ro6 24 | 7eTSbfIguycAAwcH/2waLIQR8qYKxPknNuSdsOOqF2jf2gglL/7uMsIzjfkFzgHo 25 | +GNHw9tmlZqD3yzaZ/N7Yv08ujRHhmWPBYAWRICBM3qo0zMJ9kI5XWRobeRQpLtf 26 | YxxIOenq8R9t6YU9ryHdqf+P+Fi38eN5ERTDhNLrJOnO5/TA+of97BWCmdtJMlWM 27 | RaHtqXxwo02Yi65IqKx6L7oOvT7Gh4NV2eglz8ZafPEoP8+V8ER7rwBYPiLk4Mse 28 | oVImjveq2dmLUip9OPwznoaeyC8zB0mJ13m/KNC+CffkBgoXpMPiKzbu5YTjVw++ 29 | MlEmfgL42yDK0hnokmW2i9y1RBh/T1VQQeQbUaeITAQYEQIADAUCRM3ITgUJCWYB 30 | gAAKCRCaemCth7qvrcbtAJ9j3C6lKNRB3uKcrfze66jAVQh0qACaAysOK82TcQ/2 31 | 73ryR0xWMFnpGqg= 32 | =bMTb 33 | -----END PGP PUBLIC KEY BLOCK----- 34 | 35 | Mark Hibberd 36 | mark@hibberd.id.au 37 | http://mth.io 38 | 39 | Dylan Just 40 | dylan@techtangents.com 41 | 42 | Sean Parsons 43 | github@futurenotfound.com 44 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | This code has a number of copyright owners. The initial codebase was copyright Ephox Pty Ltd, 2 | with several additions made by Tony Morris, Sean Parsons and Mark Hibberd. 3 | 4 | The initial code and subsequent changes are licensed under a 3-point BSD style license. 5 | 6 | See LICENCE or https://github.com/argonaut-io/argonaut/blob/master/LICENCE. -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Ephox Pty Ltd, Mark Hibberd, Sean Parsons and other contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. The name of the author may not be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Argonaut 2 | 3 | [![argonaut Scala version support](https://index.scala-lang.org/argonaut-io/argonaut/argonaut/latest-by-scala-version.svg)](https://index.scala-lang.org/argonaut-io/argonaut/argonaut) 4 | 5 | 6 | ### What is Argonaut? 7 | 8 | Argonaut is a JSON library for Scala, providing a rich library for parsing, printing and manipulation as well as convenient codecs for translation to and from scala data types. 9 | 10 | Argonaut is licenced under BSD3 (see `LICENCE`). 11 | 12 | 13 | ### Documentation 14 | 15 | * [Scala Docs](https://javadoc.io/doc/io.github.argonaut-io/argonaut_2.13/latest/index.html) 16 | * [Examples](https://github.com/argonaut-io/argonaut/tree/master/argonaut/shared/src/test/scala/argonaut/example) 17 | 18 | 19 | ### SBT Settings 20 | 21 | Just add argonaut as a dependency. 22 | 23 | Stable: 24 | 25 | ```scala 26 | libraryDependencies += "io.github.argonaut-io" %% "argonaut" % "6.3.11" 27 | ``` 28 | 29 | Note that the 6.2.x releases supports scala `2.11.*`, `2.12.*` and `2.13.*` with scalaz `7.2.*`. 30 | 31 | Note that the 6.3.x releases supports scala `2.12.*`, `2.13.*` and `3.x` with scalaz `7.3.*`. 32 | 33 | 34 | ### Release 35 | 36 | For a snapshot build run: 37 | ./sbt +publish 38 | 39 | For a release build run: 40 | 41 | ./sbt "release cross" 42 | 43 | Note for a release build you will want to enter the details for the 44 | release build number and then the subsequent build number. At this 45 | step it is fine to enter the original build number as the next number 46 | (for example when doing Milestone or RC builds). As an example: 47 | 48 | Release version [6.0] : 6.0-M3 49 | Next version [6.1-SNAPSHOT] : 6.0-SNAPSHOT 50 | 51 | 52 | ### Provenance 53 | 54 | Argonaut was initially developed to support products at [Ephox](http://ephox.com), who have now kindly relinquished control to the community. 55 | 56 | The library was open-sourced under a [BSD License](https://github.com/argonaut-io/argonaut/blob/master/LICENSE), drawing users, support and improvements from a number of contributors. 57 | -------------------------------------------------------------------------------- /argonaut-benchmark/src/main/resources/json-data/README: -------------------------------------------------------------------------------- 1 | This data comes from benchmarks for the haskell json library "aeson". 2 | 3 | https://github.com/bos/aeson/tree/master/benchmarks/json-data 4 | 5 | 6 | -------------------------------------------------------------------------------- /argonaut-benchmark/src/main/resources/json-data/example.json: -------------------------------------------------------------------------------- 1 | {"web-app": { 2 | "servlet": [ 3 | { 4 | "servlet-name": "cofaxCDS", 5 | "servlet-class": "org.cofax.cds.CDSServlet", 6 | "init-param": { 7 | "configGlossary:installationAt": "Philadelphia, PA", 8 | "configGlossary:adminEmail": "ksm@pobox.com", 9 | "configGlossary:poweredBy": "Cofax", 10 | "configGlossary:poweredByIcon": "/images/cofax.gif", 11 | "configGlossary:staticPath": "/content/static", 12 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 13 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 14 | "templatePath": "templates", 15 | "templateOverridePath": "", 16 | "defaultListTemplate": "listTemplate.htm", 17 | "defaultFileTemplate": "articleTemplate.htm", 18 | "useJSP": false, 19 | "jspListTemplate": "listTemplate.jsp", 20 | "jspFileTemplate": "articleTemplate.jsp", 21 | "cachePackageTagsTrack": 200, 22 | "cachePackageTagsStore": 200, 23 | "cachePackageTagsRefresh": 60, 24 | "cacheTemplatesTrack": 100, 25 | "cacheTemplatesStore": 50, 26 | "cacheTemplatesRefresh": 15, 27 | "cachePagesTrack": 200, 28 | "cachePagesStore": 100, 29 | "cachePagesRefresh": 10, 30 | "cachePagesDirtyRead": 10, 31 | "searchEngineListTemplate": "forSearchEnginesList.htm", 32 | "searchEngineFileTemplate": "forSearchEngines.htm", 33 | "searchEngineRobotsDb": "WEB-INF/robots.db", 34 | "useDataStore": true, 35 | "dataStoreClass": "org.cofax.SqlDataStore", 36 | "redirectionClass": "org.cofax.SqlRedirection", 37 | "dataStoreName": "cofax", 38 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 39 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 40 | "dataStoreUser": "sa", 41 | "dataStorePassword": "dataStoreTestQuery", 42 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 43 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 44 | "dataStoreInitConns": 10, 45 | "dataStoreMaxConns": 100, 46 | "dataStoreConnUsageLimit": 100, 47 | "dataStoreLogLevel": "debug", 48 | "maxUrlLength": 500}}, 49 | { 50 | "servlet-name": "cofaxEmail", 51 | "servlet-class": "org.cofax.cds.EmailServlet", 52 | "init-param": { 53 | "mailHost": "mail1", 54 | "mailHostOverride": "mail2"}}, 55 | { 56 | "servlet-name": "cofaxAdmin", 57 | "servlet-class": "org.cofax.cds.AdminServlet"}, 58 | 59 | { 60 | "servlet-name": "fileServlet", 61 | "servlet-class": "org.cofax.cds.FileServlet"}, 62 | { 63 | "servlet-name": "cofaxTools", 64 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 65 | "init-param": { 66 | "templatePath": "toolstemplates/", 67 | "log": 1, 68 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 69 | "logMaxSize": "", 70 | "dataLog": 1, 71 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 72 | "dataLogMaxSize": "", 73 | "removePageCache": "/content/admin/remove?cache=pages&id=", 74 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 75 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 76 | "lookInContext": 1, 77 | "adminGroupID": 4, 78 | "betaServer": true}}], 79 | "servlet-mapping": { 80 | "cofaxCDS": "/", 81 | "cofaxEmail": "/cofaxutil/aemail/*", 82 | "cofaxAdmin": "/admin/*", 83 | "fileServlet": "/static/*", 84 | "cofaxTools": "/tools/*"}, 85 | 86 | "taglib": { 87 | "taglib-uri": "cofax.tld", 88 | "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} 89 | -------------------------------------------------------------------------------- /argonaut-benchmark/src/main/resources/json-data/twitter1.json: -------------------------------------------------------------------------------- 1 | {"results":[{"from_user_id_str":"80430860","profile_image_url":"http://a2.twimg.com/profile_images/536455139/icon32_normal.png","created_at":"Wed, 26 Jan 2011 07:07:02 +0000","from_user":"kazu_yamamoto","id_str":"30159761706061824","metadata":{"result_type":"recent"},"to_user_id":null,"text":"Haskell Server Pages \u3063\u3066\u3001\u307e\u3060\u7d9a\u3044\u3066\u3044\u305f\u306e\u304b\uff01","id":30159761706061824,"from_user_id":80430860,"geo":null,"iso_language_code":"no","to_user_id_str":null,"source":"<a href="http://twitter.com/">web</a>"}],"max_id":30159761706061824,"since_id":0,"refresh_url":"?since_id=30159761706061824&q=haskell","next_page":"?page=2&max_id=30159761706061824&rpp=1&q=haskell","results_per_page":1,"page":1,"completed_in":0.012606,"since_id_str":"0","max_id_str":"30159761706061824","query":"haskell"} -------------------------------------------------------------------------------- /argonaut-benchmark/src/main/scala/argonaut/benchmark/Benchmark.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | package benchmark 3 | 4 | object Benchmark { 5 | def time(run: () => Unit, count: Int = 1, preRunCount: Int = 500) = { 6 | for (i <- 1 to preRunCount) run() 7 | val s = System.currentTimeMillis 8 | for (i <- 1 to count) run() 9 | val e = System.currentTimeMillis 10 | e - s 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /argonaut-benchmark/src/main/scala/argonaut/benchmark/CaliperBenchmark.scala: -------------------------------------------------------------------------------- 1 | package argonaut.benchmark 2 | 3 | import com.google.caliper.* 4 | import argonaut.* 5 | import Argonaut.* 6 | import com.fasterxml.jackson.core.TreeNode 7 | import com.fasterxml.jackson.core.JsonFactory as JacksonJsonFactory 8 | 9 | // Stolen conveniently from 10 | // https://github.com/sirthias/scala-benchmarking-template/blob/master/src/main/scala/org/example/SimpleScalaBenchmark.scala. 11 | trait SimpleScalaBenchmark extends SimpleBenchmark { 12 | 13 | // helper method to keep the actual benchmarking methods a bit cleaner 14 | // your code snippet should always return a value that cannot be "optimized away" 15 | def repeat[@specialized A](reps: Int)(snippet: => A) = { 16 | val zero = 0.asInstanceOf[A] // looks weird but does what it should: init w/ default value in a fully generic way 17 | var i = 0 18 | var result = zero 19 | while (i < reps) { 20 | val res = snippet 21 | if (res != zero) result = res // make result depend on the benchmarking snippet result 22 | i = i + 1 23 | } 24 | result 25 | } 26 | } 27 | 28 | object CaliperArgonautBenchmarkRunner { 29 | def main(args: Array[String]): Unit = { 30 | Runner.main(classOf[CaliperArgonautBenchmark], args) 31 | } 32 | } 33 | 34 | object CaliperJacksonBenchmarkRunner { 35 | def main(args: Array[String]): Unit = { 36 | Runner.main(classOf[CaliperJacksonBenchmark], args) 37 | } 38 | } 39 | 40 | case class CaliperArgonautBenchmark() extends CaliperBenchmark { 41 | override def repeatParse(json: String, reps: Int): Unit = repeat(reps)(json.parse) 42 | 43 | val largeString = Data.largestring.parseOption.get 44 | val manyStrings = Data.manystrings.parseOption.get 45 | 46 | def timelargestringnospaces(reps: Int) = repeat(reps) { 47 | largeString.nospaces.length 48 | } 49 | 50 | def timemanystringsnospaces(reps: Int) = repeat(reps) { 51 | manyStrings.nospaces.length 52 | } 53 | 54 | val jsonToPrint = Data.apachebuilds.parseOption.get 55 | val smallJsonToPrint = jSingleObject("array", jArray(jNumber(5) :: List(jTrue, jFalse))) 56 | def timesmallnospaces(reps: Int) = repeat(reps) { 57 | smallJsonToPrint.nospaces.length 58 | } 59 | def timesmallspaces4(reps: Int) = repeat(reps) { 60 | smallJsonToPrint.spaces4.length 61 | } 62 | def timenospaces(reps: Int) = repeat(reps) { 63 | jsonToPrint.nospaces.length 64 | } 65 | def timespaces4(reps: Int) = repeat(reps) { 66 | jsonToPrint.spaces4.length 67 | } 68 | } 69 | 70 | object CaliperJacksonBenchmark { 71 | val jsonFactory = new JacksonJsonFactory() 72 | } 73 | 74 | case class CaliperJacksonBenchmark() extends CaliperBenchmark { 75 | override def repeatParse(json: String, reps: Int): Unit = repeat(reps) { 76 | val parser = CaliperJacksonBenchmark.jsonFactory.createParser(json) 77 | if (parser.readValueAsTree[TreeNode]().asToken() == null) 0 else 1 78 | } 79 | } 80 | 81 | object ArgonautSimpleBench { 82 | def main(args: Array[String]): Unit = { 83 | Thread.sleep(10000) 84 | (0 to 3000).foldLeft(0L) { (left, right) => left + Data.example.parseOption.get.spaces4.length + right } 85 | } 86 | } 87 | 88 | trait CaliperBenchmark extends SimpleScalaBenchmark { 89 | def repeatParse(json: String, reps: Int): Unit 90 | 91 | def timeexample(reps: Int) = repeatParse(Data.example, reps) 92 | def timeintegers(reps: Int) = repeatParse(Data.integers, reps) 93 | def timejp10(reps: Int) = repeatParse(Data.jp10, reps) 94 | def timejp100(reps: Int) = repeatParse(Data.jp100, reps) 95 | def timejp50(reps: Int) = repeatParse(Data.jp50, reps) 96 | def timenumbers(reps: Int) = repeatParse(Data.numbers, reps) 97 | def timetwitter1(reps: Int) = repeatParse(Data.twitter1, reps) 98 | def timetwitter10(reps: Int) = repeatParse(Data.twitter10, reps) 99 | def timetwitter100(reps: Int) = repeatParse(Data.twitter100, reps) 100 | def timetwitter20(reps: Int) = repeatParse(Data.twitter20, reps) 101 | def timetwitter50(reps: Int) = repeatParse(Data.twitter50, reps) 102 | def timeapachebuilds(reps: Int) = repeatParse(Data.apachebuilds, reps) 103 | def timelargestring(reps: Int) = repeatParse(Data.largestring, reps) 104 | def timemanystrings(reps: Int) = repeatParse(Data.manystrings, reps) 105 | } 106 | -------------------------------------------------------------------------------- /argonaut-benchmark/src/main/scala/argonaut/benchmark/Data.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | package benchmark 3 | 4 | import scala.io.Source 5 | 6 | object Data { 7 | def read(n: String) = { 8 | val cls = getClass() 9 | val cl = cls.getClassLoader() 10 | val is = cl.getResourceAsStream("json-data/" + n + ".json") 11 | Source.fromInputStream(is).mkString 12 | } 13 | 14 | lazy val example = 15 | read("example") 16 | 17 | lazy val integers = 18 | read("integers") 19 | 20 | lazy val jp10 = 21 | read("jp10") 22 | 23 | lazy val jp100 = 24 | read("jp100") 25 | 26 | lazy val jp50 = 27 | read("jp50") 28 | 29 | lazy val numbers = 30 | read("numbers") 31 | 32 | lazy val twitter1 = 33 | read("twitter1") 34 | 35 | lazy val twitter10 = 36 | read("twitter10") 37 | 38 | lazy val twitter100 = 39 | read("twitter100") 40 | 41 | lazy val twitter20 = 42 | read("twitter20") 43 | 44 | lazy val twitter50 = 45 | read("twitter50") 46 | 47 | lazy val apachebuilds = 48 | read("apache_builds") 49 | 50 | lazy val largestring = 51 | read("largestring") 52 | 53 | lazy val manystrings = 54 | read("manystrings") 55 | } 56 | -------------------------------------------------------------------------------- /argonaut-benchmark/src/main/scala/argonaut/benchmark/Main.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | package benchmark 3 | 4 | object Main { 5 | def main(args: Array[String]) = { 6 | ParserBench.parseAndPrint("example", Data.example) 7 | ParserBench.parseAndPrint("integers", Data.integers) 8 | ParserBench.parseAndPrint("jp10", Data.jp10) 9 | ParserBench.parseAndPrint("jp100", Data.jp100) 10 | ParserBench.parseAndPrint("jp50", Data.jp50) 11 | ParserBench.parseAndPrint("numbers", Data.numbers) 12 | ParserBench.parseAndPrint("twitter1", Data.twitter1) 13 | ParserBench.parseAndPrint("twitter10", Data.twitter10) 14 | ParserBench.parseAndPrint("twitter100", Data.twitter100) 15 | ParserBench.parseAndPrint("twitter20", Data.twitter20) 16 | ParserBench.parseAndPrint("twitter50", Data.twitter50) 17 | ParserBench.parseAndPrint("largestring", Data.largestring) 18 | ParserBench.parseAndPrint("manystrings", Data.manystrings) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /argonaut-benchmark/src/main/scala/argonaut/benchmark/ParserBench.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | package benchmark 3 | 4 | import Argonaut.* 5 | 6 | object ParserBench { 7 | def parse(json: String, count: Int) = { 8 | val elapsed = Benchmark.time(() => json.parse, count) / 1000.0 9 | val bytes = json.getBytes("UTF-8").length 10 | val rate = (count / elapsed).toInt 11 | 12 | (bytes, rate) 13 | } 14 | 15 | def parseAndPrint(name: String, json: String, count: Int = 10000) = { 16 | parse(json, count) match { 17 | case (bytes, rate) => 18 | printf("%s, %.5gKB @ %dmsg/s (%.2gMB/s)%n", name, bytes / 1024.0, rate, rate / ((1024 * 1024) / (bytes / 1.0))) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/ACursorCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object ACursorCats extends ACursorCatss {} 4 | 5 | trait ACursorCatss {} 6 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/ArgonautCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object ArgonautCats extends ArgonautCatss 4 | 5 | trait ArgonautCatss 6 | extends ACursorCatss 7 | with CodecJsonCatss 8 | with ContextCatss 9 | with ContextElementCatss 10 | with CursorCatss 11 | with CursorHistoryCatss 12 | with CursorOpCatss 13 | with CursorOpElementCatss 14 | with DecodeJsonCatss 15 | with DecodeResultCatss 16 | with EncodeJsonCatss 17 | with HCursorCatss 18 | with JsonCatss 19 | with JsonIdentityCatss 20 | with JsonObjectCatss 21 | with PrettyParamsCatss 22 | with StringWrapCatss 23 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/CodecJsonCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object CodecJsonCats extends CodecJsonCatss {} 4 | 5 | trait CodecJsonCatss {} 6 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/ContextCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import ContextElementCats.* 4 | import JsonCats.* 5 | import cats.* 6 | import instances.all.* 7 | import syntax.eq.* 8 | import syntax.show.* 9 | 10 | object ContextCats extends ContextCatss 11 | 12 | trait ContextCatss { 13 | implicit val ContextInstances: Eq[Context] & Show[Context] = { 14 | new Eq[Context] with Show[Context] { 15 | def eqv(c1: Context, c2: Context) = { 16 | Eq.by((_: Context).toList).eqv(c1, c2) 17 | } 18 | override def show(c: Context) = { 19 | c.toList.map(_.show).mkString(".") 20 | } 21 | } 22 | } 23 | } 24 | 25 | object ContextElementCats extends ContextElementCatss 26 | 27 | trait ContextElementCatss { 28 | implicit val ContextElementInstances: Eq[ContextElement] & Show[ContextElement] = { 29 | new Eq[ContextElement] with Show[ContextElement] { 30 | override def eqv(c1: ContextElement, c2: ContextElement) = { 31 | c1 match { 32 | case ArrayContext(n1, j1) => 33 | c2 match { 34 | case ArrayContext(n2, j2) => n1 === n2 && j1 === j2 35 | case ObjectContext(_, _) => false 36 | } 37 | case ObjectContext(f1, j1) => 38 | c2 match { 39 | case ObjectContext(f2, j2) => f1 === f2 && j1 === j2 40 | case ArrayContext(_, _) => false 41 | } 42 | } 43 | } 44 | 45 | override def show(c: ContextElement) = { 46 | c match { 47 | case ArrayContext(n, j) => "[" + n + "]" 48 | case ObjectContext(f, j) => "{" + f + "}" 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/CursorCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import ContextCats.* 4 | import JsonCats.* 5 | import JsonObjectCats.* 6 | import cats.* 7 | import instances.list.* 8 | import instances.string.* 9 | import syntax.eq.* 10 | import syntax.show.* 11 | 12 | object CursorCats extends CursorCatss {} 13 | 14 | trait CursorCatss { 15 | implicit val CursorInstances: Eq[Cursor] & Show[Cursor] = new Eq[Cursor] with Show[Cursor] { 16 | def eqv(c1: Cursor, c2: Cursor) = { 17 | c1 match { 18 | case CJson(j1) => 19 | c2 match { 20 | case CJson(j2) => j1 === j2 21 | case _ => false 22 | } 23 | case CArray(p1, _, l1, j1, r1) => 24 | c2 match { 25 | case CArray(p2, _, l2, j2, r2) => p1 === p2 && l1 === l2 && j1 === j2 && r1 === r2 26 | case _ => false 27 | } 28 | case CObject(p1, _, x1, (f1, j1)) => 29 | c2 match { 30 | case CObject(p2, _, x2, (f2, j2)) => p1 === p2 && x1 === x2 && f1 === f2 && j1 === j2 31 | case _ => false 32 | } 33 | } 34 | } 35 | 36 | override def show(c: Cursor) = { 37 | c.context.show + " ==> " + c.focus.show 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/CursorHistoryCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import CursorOpCats.* 4 | import cats.* 5 | import instances.list.* 6 | import syntax.eq.* 7 | 8 | object CursorHistoryCats extends CursorHistoryCatss 9 | 10 | trait CursorHistoryCatss { 11 | implicit val CursorHistoryInstances: Show[CursorHistory] & Eq[CursorHistory] & Monoid[CursorHistory] = { 12 | new Show[CursorHistory] with Eq[CursorHistory] with Monoid[CursorHistory] { 13 | override def show(h: CursorHistory) = Show[List[CursorOp]].show(h.toList) 14 | def eqv(h1: CursorHistory, h2: CursorHistory) = { 15 | h1.toList === h2.toList 16 | } 17 | def empty = CursorHistory(List()) 18 | def combine(h1: CursorHistory, h2: CursorHistory) = { 19 | CursorHistory(h1.toList ::: h2.toList) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/CursorOpCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import CursorOpElementCats.* 4 | import cats.* 5 | import syntax.show.* 6 | 7 | object CursorOpCats extends CursorOpCatss {} 8 | 9 | trait CursorOpCatss { 10 | implicit val CursorOpInstances: Show[CursorOp] & Eq[CursorOp] = { 11 | new Show[CursorOp] with Eq[CursorOp] { 12 | override def show(x: CursorOp) = x match { 13 | case Reattempt => ".?." 14 | case El(o, s) => if (s) o.show else '*' +: '.' +: o.show 15 | } 16 | def eqv(a1: CursorOp, a2: CursorOp) = { 17 | a1 == a2 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/CursorOpElementCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import cats.* 4 | 5 | object CursorOpElementCats extends CursorOpElementCatss 6 | 7 | trait CursorOpElementCatss { 8 | implicit val CursorOpElementInstances: Show[CursorOpElement] & Eq[CursorOpElement] = { 9 | new Show[CursorOpElement] with Eq[CursorOpElement] { 10 | override def show(e: CursorOpElement) = { 11 | e match { 12 | case CursorOpLeft => "<-" 13 | case CursorOpRight => "->" 14 | case CursorOpFirst => "|<-" 15 | case CursorOpLast => "->|" 16 | case CursorOpUp => "_/" 17 | case CursorOpLeftN(n) => "-<-:(" + n + ")" 18 | case CursorOpRightN(n) => ":->-(" + n + ")" 19 | case CursorOpLeftAt(_) => "?<-:" 20 | case CursorOpRightAt(_) => ":->?" 21 | case CursorOpFind(_) => "find" 22 | case CursorOpField(f) => "--(" + f + ")" 23 | case CursorOpDownField(f) => "--\\(" + f + ")" 24 | case CursorOpDownArray => "\\\\" 25 | case CursorOpDownAt(_) => "-\\" 26 | case CursorOpDownN(n) => "=\\(" + n + ")" 27 | case CursorOpDeleteGoParent => "!_/" 28 | case CursorOpDeleteGoLeft => "<-!" 29 | case CursorOpDeleteGoRight => "!->" 30 | case CursorOpDeleteGoFirst => "|<-!" 31 | case CursorOpDeleteGoLast => "!->|" 32 | case CursorOpDeleteGoField(f) => "!--(" + f + ")" 33 | case CursorOpDeleteLefts => "!<" 34 | case CursorOpDeleteRights => ">!" 35 | case CursorOpSetLefts(_) => "!...<" 36 | case CursorOpSetRights(_) => ">...!" 37 | } 38 | } 39 | 40 | def eqv(e1: CursorOpElement, e2: CursorOpElement) = { 41 | e1 == e2 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/DecodeJsonCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import cats.data.* 4 | 5 | object DecodeJsonCats extends DecodeJsonCatss {} 6 | 7 | trait DecodeJsonCatss { 8 | implicit def NonEmptyListDecodeJson[A: DecodeJson](implicit DL: DecodeJson[List[A]]): DecodeJson[NonEmptyList[A]] = { 9 | DL.flatMap(l => 10 | DecodeJson[NonEmptyList[A]](c => 11 | NonEmptyList.fromList(l) match { 12 | case None => DecodeResult.fail("[A]NonEmptyList[A]", c.history) 13 | case Some(n) => DecodeResult.ok(n) 14 | } 15 | ) 16 | ) setName "[A]NonEmptyList[A]" 17 | } 18 | 19 | implicit def ValidatedDecodeJson[A, B](implicit DA: DecodeJson[A], DB: DecodeJson[B]): DecodeJson[Validated[A, B]] = { 20 | DecodeJson(a => { 21 | val l = (a --\ "Invalid").success 22 | val r = (a --\ "Valid").success 23 | (l, r) match { 24 | case (Some(c), None) => DA(c).map(Validated.Invalid(_)) 25 | case (None, Some(c)) => DB(c).map(Validated.Valid(_)) 26 | case _ => DecodeResult.fail("[A, B]Validated[A, B]", a.history) 27 | } 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/DecodeResultCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import CursorHistoryCats.* 4 | import cats.* 5 | import instances.tuple.* 6 | import instances.either.* 7 | import instances.string.* 8 | import instances.eq.* 9 | import syntax.contravariant.* 10 | 11 | object DecodeResultCats extends DecodeResultCatss {} 12 | 13 | trait DecodeResultCatss { 14 | 15 | type DecodeEither[A] = Either[(String, CursorHistory), A] 16 | 17 | implicit def DecodeResultEq[A](implicit EA: Eq[A]): Eq[DecodeResult[A]] = 18 | Eq[DecodeEither[A]].contramap(_.toEither) 19 | 20 | implicit def DecodeResultMonad: Monad[DecodeResult] = new Monad[DecodeResult] { 21 | def pure[A](a: A) = DecodeResult.ok(a) 22 | def flatMap[A, B](a: DecodeResult[A])(f: A => DecodeResult[B]) = a flatMap f 23 | override def tailRecM[A, B](a: A)(f: A => DecodeResult[Either[A, B]]) = 24 | DecodeResult(Monad[DecodeEither].tailRecM(a)(f andThen (_.toEither))) 25 | override def map[A, B](a: DecodeResult[A])(f: A => B) = a map f 26 | } 27 | 28 | implicit def DecodeResultShow[A](implicit SE: Show[DecodeEither[A]]): Show[DecodeResult[A]] = 29 | SE.contramap(_.toEither) 30 | } 31 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/EncodeJsonCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Argonaut.* 4 | import cats.* 5 | import data.* 6 | 7 | object EncodeJsonCats extends EncodeJsonCatss {} 8 | 9 | trait EncodeJsonCatss { 10 | def fromFoldable[F[_], A](implicit A: EncodeJson[A], F: Foldable[F]): EncodeJson[F[A]] = 11 | EncodeJson(fa => jArray(F.foldLeft(fa, Nil: List[Json])((list, a) => A.encode(a) :: list).reverse)) 12 | 13 | implicit val EncodeJsonContra: Contravariant[EncodeJson] = new Contravariant[EncodeJson] { 14 | def contramap[A, B](r: EncodeJson[A])(f: B => A) = r contramap f 15 | } 16 | 17 | implicit val EncodeJsonNumberInstance: Contravariant[EncodeJsonNumber] = new Contravariant[EncodeJsonNumber] { 18 | def contramap[A, B](r: EncodeJsonNumber[A])(f: B => A) = r contramap f 19 | } 20 | 21 | implicit val EncodePossibleJsonNumberInstance: Contravariant[EncodePossibleJsonNumber] = 22 | new Contravariant[EncodePossibleJsonNumber] { 23 | def contramap[A, B](r: EncodePossibleJsonNumber[A])(f: B => A) = r contramap f 24 | } 25 | 26 | implicit def NonEmptyListEncodeJson[A: EncodeJson]: EncodeJson[NonEmptyList[A]] = 27 | fromFoldable[NonEmptyList, A] 28 | 29 | implicit def ValidatedEncodeJson[E, A](implicit EA: EncodeJson[E], EB: EncodeJson[A]): EncodeJson[Validated[E, A]] = 30 | EncodeJson( 31 | _.fold( 32 | e => jSingleObject("Invalid", EA(e)), 33 | a => jSingleObject("Valid", EB(a)) 34 | ) 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/HCursorCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object HCursorCats extends HCursorCatss 4 | 5 | trait HCursorCatss {} 6 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/JsonCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import JsonNumberCats.* 4 | import JsonObjectCats.* 5 | import cats.* 6 | import instances.list.* 7 | import syntax.eq.* 8 | 9 | object JsonCats extends JsonCatss {} 10 | 11 | trait JsonCatss { 12 | implicit val JsonInstances: Eq[Json] & Show[Json] = { 13 | new Eq[Json] with Show[Json] { 14 | def eqv(a1: Json, a2: Json) = { 15 | a1 match { 16 | case JNull => a2.isNull 17 | case JBool(b) => a2.bool exists (_ == b) 18 | case JNumber(n) => a2.number exists (_ === n) 19 | case JString(s) => a2.string exists (_ == s) 20 | case JArray(a) => a2.array exists (_ === a) 21 | case JObject(o) => a2.obj exists (_ === o) 22 | } 23 | } 24 | 25 | override def show(a: Json): String = Show.fromToString.show(a) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/JsonIdentityCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object JsonIdentityCats extends JsonIdentityCatss 4 | 5 | trait JsonIdentityCatss {} 6 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/JsonNumberCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import cats.* 4 | 5 | object JsonNumberCats { 6 | implicit val JsonNumberEq: Eq[JsonNumber] = 7 | _ == _ 8 | } 9 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/JsonObjectCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Json.* 4 | import JsonObject.* 5 | import cats.* 6 | import syntax.foldable.* 7 | import syntax.functor.* 8 | 9 | object JsonObjectCats extends JsonObjectCatss { 10 | def from[F[_]: Foldable](f: F[(JsonField, Json)]): JsonObject = { 11 | f.foldLeft(empty) { case (acc, (k, v)) => acc + (k, v) } 12 | } 13 | } 14 | 15 | trait JsonObjectCatss { 16 | implicit val JsonObjectEq: Eq[JsonObject] = Eq.fromUniversalEquals[JsonObject] 17 | 18 | implicit val JsonObjectShow: Show[JsonObject] = Show.fromToString[JsonObject] 19 | 20 | def traverse[F[_]](o: JsonObject, f: Json => F[Json])(implicit FF: Applicative[F]): F[JsonObject] = { 21 | o.toList 22 | .foldLeft(FF.pure(List[JsonAssoc]())) { case (acc, (k, v)) => 23 | FF.map2(acc, f(v)) { (elems, newV) => 24 | (k, newV) :: elems 25 | } 26 | } 27 | .map(elems => JsonObject.fromIterable(elems.reverse)) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/PrettyParamsCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import cats.* 4 | 5 | object PrettyParamsCats extends PrettyParamsCatss 6 | 7 | trait PrettyParamsCatss { 8 | implicit val PrettyParamsEq: Eq[PrettyParams] = Eq.fromUniversalEquals 9 | } 10 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/main/scala/argonaut/StringWrapCats.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object StringWrapCats extends StringWrapCatss 4 | 5 | trait StringWrapCatss {} 6 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/test/scala/argonaut/CursorHistoryCatsSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import cats.kernel.laws.discipline.MonoidTests 4 | import arbitrary.* 5 | import CursorHistoryCats.* 6 | import org.typelevel.discipline.specs2.Discipline 7 | 8 | /** 9 | * Created by luissanchez on 27/01/2016. 10 | */ 11 | class CursorHistoryCatsSpecification extends ArgonautSpec with Discipline { 12 | def is = 13 | br ^ br ^ 14 | checkAll("CursorHistory", MonoidTests[CursorHistory].monoid) 15 | } 16 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/test/scala/argonaut/DecodeResultCatsSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import arbitrary.* 4 | import DecodeResultCats.* 5 | import cats.laws.discipline.MonadTests 6 | import org.scalacheck.* 7 | import org.scalacheck.Arbitrary.* 8 | import org.typelevel.discipline.specs2.Discipline 9 | 10 | /** 11 | * Created by luissanchez on 27/01/2016. 12 | */ 13 | class DecodeResultCatsSpecification extends ArgonautSpec with Discipline { 14 | 15 | implicit val NumGen: Gen[Int] = Gen.posNum[Int] 16 | implicit val FunGen: Gen[Int => Int] = arbFunction1[Int, Int].arbitrary 17 | 18 | def is = 19 | br ^ br ^ 20 | checkAll("DecodeResult[Int]", MonadTests[DecodeResult].monad[Int, Int, Int]) 21 | } 22 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/test/scala/argonaut/EncodeJsonCatsSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.scalacheck.* 4 | import org.scalacheck.Arbitrary.* 5 | import org.scalacheck.Prop.* 6 | 7 | /** 8 | * Created by luissanchez on 27/01/2016. 9 | */ 10 | class EncodeJsonCatsSpecification extends ArgonautSpec { 11 | 12 | def is = s2""" 13 | contravariants laws must hold for EncodeJson[Int] 14 | contravariant identity ${contravariantIdentity} 15 | contravariant composition ${contravariantcomposition} 16 | """.stripMargin 17 | 18 | def contravariantIdentity = forAll(Gen.posNum[Int]) { a => 19 | val left = EncodeJson.IntEncodeJson.contramap(identity[Int]) 20 | val right = EncodeJson.IntEncodeJson 21 | 22 | left.encode(a) must_== right.encode(a) 23 | } 24 | 25 | def contravariantcomposition = 26 | forAll(Gen.posNum[Int], arbFunction1[Int, Int].arbitrary, arbFunction1[Int, Int].arbitrary) { (a, f, g) => 27 | val left = EncodeJson.IntEncodeJson.contramap(f).contramap(g) 28 | val right = EncodeJson.IntEncodeJson.contramap(f compose g) 29 | 30 | left.encode(a) must_== right.encode(a) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/test/scala/argonaut/JsonObjectCatsSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.Data.* 4 | import argonaut.JsonObjectCats.* 5 | import cats.syntax.show.* 6 | 7 | object JsonObjectCatsSpecification extends ArgonautSpec { 8 | def is = s2""" 9 | JsonObjectCats 10 | shows ${prop((o: JsonObject) => o.show === o.toString)} 11 | """ 12 | } 13 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/test/scala/argonaut/arbitrary.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.scalacheck.* 4 | import org.scalacheck.Gen.* 5 | 6 | /** 7 | * Created by luissanchez on 27/01/2016. 8 | */ 9 | object arbitrary { 10 | 11 | // Some CursorOpElement 12 | val cursorOpElementCursorOpLeft: Gen[CursorOpElement] = Gen.const(CursorOpLeft) 13 | val cursorOpElementCursorOpRight: Gen[CursorOpElement] = Gen.const(CursorOpRight) 14 | val cursorOpElementCursorOpFirst: Gen[CursorOpElement] = Gen.const(CursorOpFirst) 15 | val cursorOpElementCursorOpLast: Gen[CursorOpElement] = Gen.const(CursorOpLast) 16 | val cursorOpElementCursorOpUp: Gen[CursorOpElement] = Gen.const(CursorOpUp) 17 | 18 | implicit val cursorOpElementGen: Gen[CursorOpElement] = Gen.oneOf( 19 | cursorOpElementCursorOpLeft, 20 | cursorOpElementCursorOpRight, 21 | cursorOpElementCursorOpFirst, 22 | cursorOpElementCursorOpLast, 23 | cursorOpElementCursorOpUp 24 | ) 25 | // CursorOp 26 | def cursorOpEl(implicit F: Gen[CursorOpElement]): Gen[CursorOp] = 27 | zip(F, Gen.oneOf(true, false)).map((El.apply _).tupled) 28 | 29 | val cursorOpReattempt: Gen[CursorOp] = Gen.const(Reattempt) 30 | 31 | implicit val cursorOpsGen: Gen[CursorOp] = Gen.oneOf(cursorOpEl, cursorOpReattempt) 32 | 33 | implicit val cursorHistoryGen: Gen[CursorHistory] = Gen.listOf(cursorOpsGen).map(CursorHistory(_)) 34 | 35 | implicit val cursorHistoryArb: Arbitrary[CursorHistory] = Arbitrary(cursorHistoryGen) 36 | 37 | implicit def decodeResultGen[A](implicit GenA: Gen[A]): Gen[DecodeResult[A]] = 38 | for { 39 | string <- alphaStr 40 | cursorHistory <- cursorHistoryGen 41 | a <- GenA 42 | generated <- Gen.oneOf(Left((string, cursorHistory)), Right(a)) 43 | } yield DecodeResult(generated) 44 | 45 | implicit def decodeResultArb[A](implicit GenA: Gen[A]): Arbitrary[DecodeResult[A]] = Arbitrary(decodeResultGen(GenA)) 46 | } 47 | -------------------------------------------------------------------------------- /argonaut-cats/shared/src/test/scala/org/typelevel/discipline/specs2/Discipline.scala: -------------------------------------------------------------------------------- 1 | package org.typelevel.discipline 2 | package specs2 3 | 4 | import org.specs2.ScalaCheck 5 | import org.specs2.SpecificationLike 6 | import org.specs2.specification.core.Fragments 7 | import org.specs2.scalacheck.Parameters 8 | 9 | // https://github.com/typelevel/discipline-specs2/blob/a04b6b05a73df404cf96696cb9cf414fc440ee17/core/src/main/scala/org/discipline/discipline/specs2/Discipline.scala 10 | trait Discipline extends ScalaCheck { self: SpecificationLike => 11 | 12 | def checkAll(name: String, ruleSet: Laws#RuleSet)(implicit p: Parameters) = { 13 | s"""${ruleSet.name} laws must hold for ${name}""" ^ br ^ t ^ 14 | Fragments.foreach(ruleSet.all.properties.toList) { case (id, prop) => 15 | id ! check(prop, p, defaultFreqMapPretty) ^ br 16 | } ^ br ^ bt 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /argonaut-jawn/shared/src/main/scala/argonaut/JawnParser.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scala.collection.mutable 4 | import org.typelevel.jawn.Facade 5 | import org.typelevel.jawn.FContext 6 | import org.typelevel.jawn.SupportParser 7 | 8 | object JawnParser extends SupportParser[Json] { 9 | implicit val facade: Facade[Json] = 10 | new Facade.NoIndexFacade[Json] { 11 | def jnull = Json.jNull 12 | def jfalse = Json.jFalse 13 | def jtrue = Json.jTrue 14 | def jnum(s: CharSequence, decIndex: Int, expIndex: Int) = Json.jNumber(JsonNumber.unsafeDecimal(s.toString)) 15 | def jstring(s: CharSequence) = Json.jString(s.toString) 16 | 17 | def singleContext(): FContext[Json] = new FContext.NoIndexFContext[Json] { 18 | var value: Json = null 19 | def add(s: CharSequence) = { value = jstring(s.toString) } 20 | def add(v: Json) = { value = v } 21 | def finish(): Json = value 22 | def isObj: Boolean = false 23 | } 24 | 25 | def arrayContext(): FContext[Json] = new FContext.NoIndexFContext[Json] { 26 | val vs = mutable.ListBuffer.empty[Json] 27 | def add(s: CharSequence) = { vs += jstring(s.toString) } 28 | def add(v: Json) = { vs += v } 29 | def finish(): Json = Json.jArray(vs.toList) 30 | def isObj: Boolean = false 31 | } 32 | 33 | def objectContext(): FContext[Json] = new FContext.NoIndexFContext[Json] { 34 | var key: String = null 35 | var vs = JsonObject.empty 36 | def add(s: CharSequence): Unit = 37 | if (key == null) { key = s.toString } 38 | else { vs = vs + (key, jstring(s.toString)); key = null } 39 | def add(v: Json): Unit = { vs = vs + (key, v); key = null } 40 | def finish() = Json.jObject(vs) 41 | def isObj = true 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /argonaut-jawn/shared/src/test/scala/argonaut/JawnParserSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.TestCompat.* 4 | import scala.util.Try 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Arbitrary.arbitrary 7 | import Argonaut.* 8 | 9 | object JawnParserSpecification { 10 | case class Example(a: Int, b: Long, c: Double) 11 | 12 | val exampleCodecJson: CodecJson[Example] = 13 | casecodec3(Example.apply, (_: Example).asTuple)("a", "b", "c") 14 | 15 | implicit val exampleCaseClassArbitrary: Arbitrary[Example] = Arbitrary( 16 | for { 17 | a <- arbitrary[Int] 18 | b <- arbitrary[Long] 19 | c <- arbitrary[Double] 20 | } yield Example(a, b, c) 21 | ) 22 | } 23 | 24 | class JawnParserSpecification extends ArgonautSpec { 25 | import JawnParserSpecification.* 26 | import JawnParser.facade 27 | 28 | def is = s2""" 29 | The JawnParser 30 | correctly marshal case classes with Long values $marshal 31 | """ 32 | 33 | def marshal = 34 | prop { (e: Example) => 35 | val jsonString: String = exampleCodecJson.encode(e).nospaces 36 | val json: Try[Json] = org.typelevel.jawn.Parser.parseFromString(jsonString) 37 | exampleCodecJson.decodeJson(json.get).toOption match { 38 | case None => ko("did not parse") 39 | case Some(example) => example must_== e 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/ACursorMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import monocle.macros.GenIso 4 | import monocle.Iso 5 | import monocle.Prism 6 | import monocle.std.either 7 | 8 | object ACursorMonocle extends ACursorMonocles 9 | 10 | trait ACursorMonocles { 11 | val aCursor: Iso[ACursor, Either[HCursor, HCursor]] = GenIso[ACursor, Either[HCursor, HCursor]] 12 | val hSuccess: Prism[ACursor, HCursor] = aCursor andThen either.stdRight[HCursor, HCursor] 13 | val hFail: Prism[ACursor, HCursor] = aCursor andThen either.stdLeft[HCursor, HCursor] 14 | } 15 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/ArgonautMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object ArgonautMonocle extends ArgonautMonocles 4 | 5 | trait ArgonautMonocles 6 | extends ACursorMonocles 7 | with CursorHistoryMonocles 8 | with CursorOpMonocles 9 | with DecodeResultMonocles 10 | with HCursorMonocles 11 | with JsonMonocles 12 | with JsonNumberMonocles 13 | with JsonObjectMonocles 14 | with JsonPaths 15 | with PrettyParamsMonocles 16 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/CursorHistoryMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import monocle.Iso 4 | import monocle.macros.GenIso 5 | 6 | object CursorHistoryMonocle extends CursorHistoryMonocles 7 | 8 | trait CursorHistoryMonocles { 9 | val cursorHistory: Iso[CursorHistory, List[CursorOp]] = GenIso[CursorHistory, List[CursorOp]] 10 | } 11 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/CursorOpMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import monocle.Prism 4 | import monocle.macros.GenPrism 5 | import monocle.macros.GenIso 6 | 7 | object CursorOpMonocle extends CursorOpMonocles 8 | 9 | trait CursorOpMonocles { 10 | val reattempt: Prism[CursorOp, Unit] = GenPrism[CursorOp, Reattempt.type] andThen GenIso.unit[Reattempt.type] 11 | val el: Prism[CursorOp, (CursorOpElement, Boolean)] = Prism[CursorOp, (CursorOpElement, Boolean)] { 12 | case Reattempt => None 13 | case El(op, success) => Some((op, success)) 14 | } { case (op, success) => El(op, success) } 15 | } 16 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/DecodeResultMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import monocle.std.either 4 | import monocle.Iso 5 | import monocle.Prism 6 | 7 | object DecodeResultMonocle extends DecodeResultMonocles 8 | 9 | trait DecodeResultMonocles { 10 | def decodeResult[A]: Iso[DecodeResult[A], Either[(String, CursorHistory), A]] = 11 | Iso[DecodeResult[A], Either[(String, CursorHistory), A]](_.toEither)(DecodeResult(_)) 12 | 13 | def success[A]: Prism[DecodeResult[A], A] = 14 | decodeResult andThen either.stdRight[(String, CursorHistory), A] 15 | 16 | def fail[A]: Prism[DecodeResult[A], (String, CursorHistory)] = 17 | decodeResult andThen either.stdLeft[(String, CursorHistory), A] 18 | } 19 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/HCursorMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import monocle.Lens 4 | import monocle.macros.GenLens 5 | 6 | object HCursorMonocle extends HCursorMonocles 7 | 8 | trait HCursorMonocles { 9 | val cursor: Lens[HCursor, Cursor] = GenLens[HCursor](_.cursor) 10 | val history: Lens[HCursor, CursorHistory] = GenLens[HCursor](_.history) 11 | } 12 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/JsonMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.Json.* 4 | import argonaut.JsonObjectMonocle.* 5 | import monocle.function.Each 6 | import monocle.function.Plated 7 | import monocle.Prism 8 | import monocle.Traversal 9 | import cats.Applicative 10 | import cats.syntax.all.* 11 | 12 | object JsonMonocle extends JsonMonocles 13 | 14 | trait JsonMonocles { 15 | 16 | /** A Prism for JSON Null values. */ 17 | val jNullPrism: Prism[Json, Unit] = 18 | Prism[Json, Unit]( 19 | _.fold(Some(()), _ => None, _ => None, _ => None, _ => None, _ => None) 20 | )(_ => jNull) 21 | 22 | /** A Prism for JSON boolean values. */ 23 | val jBoolPrism: Prism[Json, JsonBoolean] = 24 | Prism[Json, JsonBoolean]( 25 | _.fold(None, b => Some(b), _ => None, _ => None, _ => None, _ => None) 26 | )(jBool) 27 | 28 | /** A Prism for JSON number values. */ 29 | val jNumberPrism: Prism[Json, JsonNumber] = 30 | Prism[Json, JsonNumber]( 31 | _.fold(None, _ => None, n => Some(n), _ => None, _ => None, _ => None) 32 | )(jNumber) 33 | 34 | /** A Prism for JSON number values. */ 35 | val jBigDecimalPrism: Prism[Json, BigDecimal] = 36 | jNumberPrism andThen JsonNumberMonocle.jNumberToBigDecimal 37 | 38 | /** 39 | * An Optional for JSON number values based on Doubles. 40 | */ 41 | // val jDoubleOptional: Optional[Json, Double] = 42 | // jNumberPrism andThen JsonNumberMonocle.jNumberToDouble 43 | 44 | /** 45 | * An Optional for JSON number values based on Floats. 46 | */ 47 | // val jFloatOptional: Optional[Json, Float] = 48 | // jNumberPrism andThen JsonNumberMonocle.jNumberToFloat 49 | 50 | /** A Prism for JSON BigInt values. */ 51 | val jBigIntPrism: Prism[Json, BigInt] = 52 | jNumberPrism andThen JsonNumberMonocle.jNumberToBigInt 53 | 54 | /** A Prism for JSON Long values. */ 55 | val jLongPrism: Prism[Json, Long] = 56 | jNumberPrism andThen JsonNumberMonocle.jNumberToLong 57 | 58 | /** A Prism for JSON Int values. */ 59 | val jIntPrism: Prism[Json, Int] = 60 | jNumberPrism andThen JsonNumberMonocle.jNumberToInt 61 | 62 | /** A Prism for JSON Short values. */ 63 | val jShortPrism: Prism[Json, Short] = 64 | jNumberPrism andThen JsonNumberMonocle.jNumberToShort 65 | 66 | /** A Prism for JSON Byte values. */ 67 | def jBytePrism: Prism[Json, Byte] = 68 | jNumberPrism andThen JsonNumberMonocle.jNumberToByte 69 | 70 | /** A Prism for JSON string values. */ 71 | val jStringPrism: Prism[Json, JsonString] = 72 | Prism[Json, JsonString]( 73 | _.fold(None, _ => None, _ => None, s => Some(s), _ => None, _ => None) 74 | )(jString) 75 | 76 | /** A Prism for JSON array values. */ 77 | val jArrayPrism: Prism[Json, JsonArray] = 78 | Prism[Json, JsonArray]( 79 | _.fold(None, _ => None, _ => None, _ => None, a => Some(a), _ => None) 80 | )(jArray) 81 | 82 | /** A Prism for JSON object values. */ 83 | val jObjectPrism: Prism[Json, JsonObject] = 84 | Prism[Json, JsonObject]( 85 | _.fold(None, _ => None, _ => None, _ => None, _ => None, o => Some(o)) 86 | )(jObject) 87 | 88 | /** a Traversal to all values of a JsonObject or JsonList */ 89 | val jDescendants: Traversal[Json, Json] = new Traversal[Json, Json] { 90 | override def modifyA[F[_]](f: Json => F[Json])(s: Json)(implicit F: Applicative[F]): F[Json] = 91 | s.fold( 92 | F.pure(s), 93 | _ => F.pure(s), 94 | _ => F.pure(s), 95 | _ => F.pure(s), 96 | arr => Each.each[List[Json], Json].modifyA(f)(arr).map(Json.array(_*)), 97 | obj => Each.each[JsonObject, Json].modifyA(f)(obj).map(Json.jObject) 98 | ) 99 | } 100 | 101 | implicit lazy val jsonPlated: Plated[Json] = new Plated[Json] { 102 | val plate: Traversal[Json, Json] = new Traversal[Json, Json] { 103 | def modifyA[F[_]](f: Json => F[Json])(a: Json)(implicit F: Applicative[F]): F[Json] = { 104 | a.fold( 105 | F.pure(a), 106 | b => F.pure(jBool(b)), 107 | n => F.pure(jNumber(n)), 108 | s => F.pure(jString(s)), 109 | _.traverse(f).map(jArray), 110 | JsonObjectCats.traverse(_, f).map(jObject) 111 | ) 112 | } 113 | } 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/JsonNumberMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import java.math.MathContext 4 | import monocle.Iso 5 | import monocle.Prism 6 | 7 | object JsonNumberMonocle extends JsonNumberMonocles 8 | 9 | trait JsonNumberMonocles { 10 | // val jNumberToDouble: Optional[JsonNumber, Double] = 11 | // Optional[JsonNumber, Double](_.toDouble)(d => n => d.asPossibleJsonNumber.getOrElse(n)) 12 | 13 | // val jNumberToFloat: Optional[JsonNumber, Float] = 14 | // Optional[JsonNumber, Float](_.toFloat)(f => n => f.asPossibleJsonNumber.getOrElse(n)) 15 | 16 | val jNumberToBigInt: Prism[JsonNumber, BigInt] = 17 | Prism[JsonNumber, BigInt](_.toBigInt)(bi => JsonBigDecimal(BigDecimal(bi, MathContext.UNLIMITED))) 18 | 19 | val jNumberToLong: Prism[JsonNumber, Long] = 20 | Prism[JsonNumber, Long](_.toLong)(JsonBigDecimal.apply _ compose BigDecimal.apply) 21 | 22 | val jNumberToInt: Prism[JsonNumber, Int] = 23 | Prism[JsonNumber, Int](_.toInt)(JsonBigDecimal.apply _ compose BigDecimal.apply) 24 | 25 | val jNumberToShort: Prism[JsonNumber, Short] = 26 | Prism[JsonNumber, Short](_.toShort)(s => JsonBigDecimal(BigDecimal(s))) 27 | 28 | val jNumberToByte: Prism[JsonNumber, Byte] = 29 | Prism[JsonNumber, Byte](_.toByte)(b => JsonBigDecimal(BigDecimal(b))) 30 | 31 | val jNumberToBigDecimal: Iso[JsonNumber, BigDecimal] = 32 | Iso[JsonNumber, BigDecimal](_.toBigDecimal)(JsonBigDecimal.apply) 33 | } 34 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/JsonObjectMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.Json.* 4 | import monocle.function.At 5 | import monocle.function.Each 6 | import monocle.function.FilterIndex 7 | import monocle.function.Index 8 | import monocle.Lens 9 | import monocle.Traversal 10 | import cats.Applicative 11 | import cats.syntax.all.* 12 | 13 | object JsonObjectMonocle extends JsonObjectMonocles 14 | 15 | trait JsonObjectMonocles { 16 | implicit val jObjectEach: Each[JsonObject, Json] = new Each[JsonObject, Json] { 17 | def each: Traversal[JsonObject, Json] = new Traversal[JsonObject, Json] { 18 | def modifyA[F[_]: Applicative](f: Json => F[Json])(from: JsonObject): F[JsonObject] = { 19 | JsonObjectCats.traverse(from, f) 20 | } 21 | } 22 | } 23 | 24 | implicit val jObjectAt: At[JsonObject, JsonField, Option[Json]] = new At[JsonObject, JsonField, Option[Json]] { 25 | def at(field: JsonField): Lens[JsonObject, Option[Json]] = 26 | monocle.Lens[JsonObject, Option[Json]](_.apply(field))(optVal => 27 | jObj => optVal.fold(jObj - field)(value => jObj + (field, value)) 28 | ) 29 | } 30 | 31 | implicit val jObjectFilterIndex: FilterIndex[JsonObject, JsonField, Json] = 32 | new FilterIndex[JsonObject, JsonField, Json] { 33 | def filterIndex(predicate: JsonField => Boolean): Traversal[JsonObject, Json] = new Traversal[JsonObject, Json] { 34 | def modifyA[F[_]: Applicative](f: Json => F[Json])(from: JsonObject): F[JsonObject] = 35 | Applicative[F].map( 36 | from.toList.traverse[F, (JsonField, Json)] { case (field, json) => 37 | Applicative[F].map(if (predicate(field)) f(json) else json.pure[F])(field -> _) 38 | } 39 | )(JsonObject.fromIterable(_)) 40 | } 41 | } 42 | 43 | implicit val jObjectIndex: Index[JsonObject, JsonField, Json] = Index.fromAt 44 | } 45 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/main/scala/argonaut/PrettyParamsMonocle.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import monocle.Lens 4 | import monocle.macros.GenLens 5 | 6 | object PrettyParamsMonocle extends PrettyParamsMonocles 7 | 8 | trait PrettyParamsMonocles { 9 | 10 | val indent: Lens[PrettyParams, String] = GenLens[PrettyParams](_.indent) 11 | val lbraceLeft: Lens[PrettyParams, String] = GenLens[PrettyParams](_.lbraceLeft) 12 | val lbraceRight: Lens[PrettyParams, String] = GenLens[PrettyParams](_.lbraceRight) 13 | val rbraceLeft: Lens[PrettyParams, String] = GenLens[PrettyParams](_.rbraceLeft) 14 | val rbraceRight: Lens[PrettyParams, String] = GenLens[PrettyParams](_.rbraceRight) 15 | val lbracketLeft: Lens[PrettyParams, String] = GenLens[PrettyParams](_.lbracketLeft) 16 | val lbracketRight: Lens[PrettyParams, String] = GenLens[PrettyParams](_.lbracketRight) 17 | val rbracketLeft: Lens[PrettyParams, String] = GenLens[PrettyParams](_.rbracketLeft) 18 | val rbracketRight: Lens[PrettyParams, String] = GenLens[PrettyParams](_.rbracketRight) 19 | val lrbracketsEmpty: Lens[PrettyParams, String] = GenLens[PrettyParams](_.lrbracketsEmpty) 20 | val arrayCommaLeft: Lens[PrettyParams, String] = GenLens[PrettyParams](_.arrayCommaLeft) 21 | val arrayCommaRight: Lens[PrettyParams, String] = GenLens[PrettyParams](_.arrayCommaRight) 22 | val objectCommaLeft: Lens[PrettyParams, String] = GenLens[PrettyParams](_.objectCommaLeft) 23 | val objectCommaRight: Lens[PrettyParams, String] = GenLens[PrettyParams](_.objectCommaRight) 24 | val colonLeft: Lens[PrettyParams, String] = GenLens[PrettyParams](_.colonLeft) 25 | val colonRight: Lens[PrettyParams, String] = GenLens[PrettyParams](_.colonRight) 26 | val preserveOrder: Lens[PrettyParams, Boolean] = GenLens[PrettyParams](_.preserveOrder) 27 | val dropNullKeys: Lens[PrettyParams, Boolean] = GenLens[PrettyParams](_.dropNullKeys) 28 | } 29 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/test/scala/argonaut/JsonMonocleSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.Data.* 4 | import argonaut.JsonMonocle.* 5 | import argonaut.JsonNumberCats.* 6 | import argonaut.JsonObjectCats.* 7 | import argonaut.JsonCats.* 8 | import monocle.function.Plated 9 | import monocle.law.discipline.PrismTests 10 | import monocle.law.discipline.TraversalTests 11 | 12 | object JsonMonocleSpecification extends ArgonautSpec { 13 | 14 | // Retired for the moment: 15 | // JsonDouble ${OptionalTests(JsonMonocle.jDoubleOptional).all} 16 | // JsonFloat ${OptionalTests(JsonMonocle.jFloatOptional).all} 17 | def is = s2""" 18 | Prism 19 | JsonNull ${PrismTests(JsonMonocle.jNullPrism).all} 20 | JsonBoolean ${PrismTests(JsonMonocle.jBoolPrism).all} 21 | JsonNumber ${PrismTests(JsonMonocle.jNumberPrism).all} 22 | JsonArray ${PrismTests(JsonMonocle.jArrayPrism).all} 23 | JsonObject ${PrismTests(JsonMonocle.jObjectPrism).all} 24 | JsonBigDecimal ${PrismTests(JsonMonocle.jBigDecimalPrism).all} 25 | JsonBigInt ${PrismTests(JsonMonocle.jBigIntPrism).all} 26 | JsonLong ${PrismTests(JsonMonocle.jLongPrism).all} 27 | JsonInt ${PrismTests(JsonMonocle.jIntPrism).all} 28 | JsonShort ${PrismTests(JsonMonocle.jShortPrism).all} 29 | JsonByte ${PrismTests(JsonMonocle.jBytePrism).all} 30 | descendants ${TraversalTests(JsonMonocle.jDescendants).all} 31 | Plated 32 | JsonPlated ${TraversalTests(Plated.plate[Json]).all} 33 | """ 34 | 35 | } 36 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/test/scala/argonaut/JsonPathSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.Argonaut.* 4 | import org.specs2.mutable.Specification 5 | import argonaut.JsonPath.root 6 | import argonaut.TestCompat.* 7 | 8 | class JsonPathSpecification extends Specification { 9 | 10 | private implicit final class MustEqualExtension[A](a1: A) { 11 | def must_==(a2: A) = a1 must beEqualTo(a2) 12 | } 13 | 14 | case class Car(model: String, maxSpeed: Int, automatic: Boolean) 15 | object Car { 16 | implicit val codec: CodecJson[Car] = CodecJson.casecodec3( 17 | Car.apply, 18 | (_: Car).asTuple 19 | )("model", "maxSpeed", "automatic") 20 | } 21 | 22 | val john: Json = Json.obj( 23 | "first_name" -> "John".asJson, 24 | "last_name" -> "Doe".asJson, 25 | "age" -> 25.asJson, 26 | "address" -> Json.obj( 27 | "street_number" -> 12.asJson, 28 | "street_name" -> "High Street".asJson 29 | ), 30 | "cars" -> Json 31 | .array( 32 | Json.obj( 33 | "model" -> "fancy".asJson, 34 | "maxSpeed" -> 120.asJson, 35 | "automatic" -> false.asJson 36 | ), 37 | Json.obj( 38 | "model" -> "suv".asJson, 39 | "maxSpeed" -> 80.asJson, 40 | "automatic" -> true.asJson 41 | ) 42 | ) 43 | .asJson 44 | ) 45 | 46 | "JsonPath" >> { 47 | "support traversal by field name" >> { 48 | root.address.street_number.int.getOption(john) must_== Some(12) 49 | } 50 | 51 | "support traversal by array index" >> { 52 | root.cars.index(1).model.string.getOption(john) must_== Some("suv") 53 | } 54 | 55 | "support traversal by array index using apply" >> { 56 | root.cars(1).model.string.getOption(john) must_== Some("suv") 57 | } 58 | 59 | "support traversal by array index using apply on the root" >> { 60 | val jsonArray = Json.array("first".asJson, "second".asJson).asJson 61 | root(0).string.getOption(jsonArray) must_== Some("first") 62 | } 63 | 64 | "support insertion and deletion" >> { 65 | root.at("first_name").replaceOption(None)(john) must_== john.obj.map(_.-("first_name")).map(jObject) 66 | root.at("foo").replace(Some(true.asJson))(john).obj.flatMap(_.apply("foo")) must_== Some(jTrue) 67 | } 68 | 69 | "support parsing json using a codec" >> { 70 | root.cars.index(0).as[Car].getAll(john) must_== List(Car("fancy", 120, automatic = false)) 71 | } 72 | } 73 | 74 | "JsonTraversalPath" >> { 75 | "support traversal over each values of a json object" >> { 76 | root.each.string.getAll(john) must_== List("John", "Doe") 77 | } 78 | 79 | "support traversal over each values of a json array" >> { 80 | root.cars.each.maxSpeed.int.getAll(john) must_== List(120, 80) 81 | } 82 | 83 | "support traversal over each values of a json array" >> { 84 | root.cars.each.maxSpeed.int.getAll(john) must_== List(120, 80) 85 | } 86 | 87 | "support filtering by field of json object" >> { 88 | root.filterByField(_.contains("first")).string.getAll(john) must_== List("John") 89 | } 90 | 91 | "support filtering by index of json array" >> { 92 | root.cars.filterByIndex(_ % 2 == 1).as[Car].getAll(john) must_== List(Car("suv", 80, automatic = true)) 93 | } 94 | 95 | "support a safe filtering by value" >> { 96 | root.cars.each.filter(root.maxSpeed.int.exist(_ > 100)).model.string.getAll(john) must_== List("fancy") 97 | } 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /argonaut-monocle/shared/src/test/scala/argonaut/PrettyParamsMonocleSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.Data.* 4 | import argonaut.PrettyParamsCats.* 5 | import monocle.law.discipline.LensTests 6 | import org.scalacheck.Arbitrary.* 7 | 8 | object PrettyParamsMonocleSpecification extends ArgonautSpec { 9 | def is = s2""" 10 | Lenses 11 | lbraceLeft $lbraceLeftLens 12 | lbraceRight $lbraceRightLens 13 | rbraceLeft $rbraceLeftLens 14 | rbraceRight $rbraceRightLens 15 | lbracketLeft $lbracketLeftLens 16 | lbracketRight $lbracketRightLens 17 | rbracketLeft $rbracketLeftLens 18 | rbracketRight $rbracketRightLens 19 | lrbracketsEmpty $lrbracketsEmptyLens 20 | arrayCommaLeft $arrayCommaLeftLens 21 | arrayCommaRight $arrayCommaRightLens 22 | objectCommaLeft $objectCommaLeftLens 23 | objectCommaRight $objectCommaRightLens 24 | colonLeft $colonLeftLens 25 | colonRight $colonRightLens 26 | preserveOrder $preserveOrderLens 27 | dropNullKeys $dropNullKeysLens 28 | """ 29 | 30 | def lbraceLeftLens = LensTests(PrettyParamsMonocle.lbraceLeft).all 31 | def lbraceRightLens = LensTests(PrettyParamsMonocle.lbraceRight).all 32 | def rbraceLeftLens = LensTests(PrettyParamsMonocle.rbraceLeft).all 33 | def rbraceRightLens = LensTests(PrettyParamsMonocle.rbraceRight).all 34 | def lbracketLeftLens = LensTests(PrettyParamsMonocle.lbracketLeft).all 35 | def lbracketRightLens = LensTests(PrettyParamsMonocle.lbracketRight).all 36 | def rbracketLeftLens = LensTests(PrettyParamsMonocle.rbracketLeft).all 37 | def rbracketRightLens = LensTests(PrettyParamsMonocle.rbracketRight).all 38 | def lrbracketsEmptyLens = LensTests(PrettyParamsMonocle.lrbracketsEmpty).all 39 | def arrayCommaLeftLens = LensTests(PrettyParamsMonocle.arrayCommaLeft).all 40 | def arrayCommaRightLens = LensTests(PrettyParamsMonocle.arrayCommaRight).all 41 | def objectCommaLeftLens = LensTests(PrettyParamsMonocle.objectCommaLeft).all 42 | def objectCommaRightLens = LensTests(PrettyParamsMonocle.objectCommaRight).all 43 | def colonLeftLens = LensTests(PrettyParamsMonocle.colonLeft).all 44 | def colonRightLens = LensTests(PrettyParamsMonocle.colonRight).all 45 | def preserveOrderLens = LensTests(PrettyParamsMonocle.preserveOrder).all 46 | def dropNullKeysLens = LensTests(PrettyParamsMonocle.dropNullKeys).all 47 | } 48 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/ACursorScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object ACursorScalaz extends ACursorScalazs {} 4 | 5 | trait ACursorScalazs {} 6 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/ArgonautScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object ArgonautScalaz extends ArgonautScalazs 4 | 5 | trait ArgonautScalazs 6 | extends ACursorScalazs 7 | with CodecJsonScalazs 8 | with ContextScalazs 9 | with ContextElementScalazs 10 | with CursorScalazs 11 | with CursorHistoryScalazs 12 | with CursorOpScalazs 13 | with CursorOpElementScalazs 14 | with DecodeJsonScalazs 15 | with DecodeResultScalazs 16 | with EncodeJsonScalazs 17 | with HCursorScalazs 18 | with JsonScalazs 19 | with JsonIdentityScalazs 20 | with JsonObjectScalazs 21 | with PrettyParamsScalazs 22 | with StringWrapScalazs 23 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/CodecJsonScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object CodecJsonScalaz extends CodecJsonScalazs {} 4 | 5 | trait CodecJsonScalazs {} 6 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/ContextScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import std.anyVal.* 5 | import std.list.* 6 | import std.string.* 7 | import syntax.show.* 8 | import syntax.equal.* 9 | import syntax.std.list.* 10 | import JsonScalaz.* 11 | import ContextElementScalaz.* 12 | 13 | object ContextScalaz extends ContextScalazs 14 | 15 | trait ContextScalazs { 16 | implicit val ContextInstances: Equal[Context] & Show[Context] = { 17 | new Equal[Context] with Show[Context] { 18 | def equal(c1: Context, c2: Context) = { 19 | Equal.equalBy((_: Context).toList).equal(c1, c2) 20 | } 21 | override def show(c: Context) = { 22 | Foldable[List].suml(c.toList.map(_.show).intersperse(Cord("."))) 23 | } 24 | } 25 | } 26 | } 27 | 28 | object ContextElementScalaz extends ContextElementScalazs 29 | 30 | trait ContextElementScalazs { 31 | implicit val ContextElementInstances: Equal[ContextElement] & Show[ContextElement] = { 32 | new Equal[ContextElement] with Show[ContextElement] { 33 | override def equal(c1: ContextElement, c2: ContextElement) = { 34 | c1 match { 35 | case ArrayContext(n1, j1) => 36 | c2 match { 37 | case ArrayContext(n2, j2) => n1 === n2 && j1 === j2 38 | case ObjectContext(_, _) => false 39 | } 40 | case ObjectContext(f1, j1) => 41 | c2 match { 42 | case ObjectContext(f2, j2) => f1 === f2 && j1 === j2 43 | case ArrayContext(_, _) => false 44 | } 45 | } 46 | } 47 | 48 | override def show(c: ContextElement) = Cord { 49 | c match { 50 | case ArrayContext(n, j) => "[" + n + "]" 51 | case ObjectContext(f, j) => "{" + f + "}" 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/CursorHistoryScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import std.list.* 5 | import syntax.equal.* 6 | import CursorOpScalaz.* 7 | 8 | object CursorHistoryScalaz extends CursorHistoryScalazs 9 | 10 | trait CursorHistoryScalazs { 11 | implicit val CursorHistoryInstances: Show[CursorHistory] & Equal[CursorHistory] & Monoid[CursorHistory] = { 12 | new Show[CursorHistory] with Equal[CursorHistory] with Monoid[CursorHistory] { 13 | override def show(h: CursorHistory) = Show[List[CursorOp]].show(h.toList) 14 | def equal(h1: CursorHistory, h2: CursorHistory) = { 15 | h1.toList === h2.toList 16 | } 17 | def zero = CursorHistory(List()) 18 | def append(h1: CursorHistory, h2: => CursorHistory) = { 19 | CursorHistory(h1.toList ::: h2.toList) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/CursorOpElementScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Json.* 4 | import scalaz.* 5 | 6 | object CursorOpElementScalaz extends CursorOpElementScalazs 7 | 8 | trait CursorOpElementScalazs { 9 | implicit val CursorOpElementInstances: Show[CursorOpElement] & Equal[CursorOpElement] = { 10 | new Show[CursorOpElement] with Equal[CursorOpElement] { 11 | override def show(e: CursorOpElement) = Cord { 12 | e match { 13 | case CursorOpLeft => "<-" 14 | case CursorOpRight => "->" 15 | case CursorOpFirst => "|<-" 16 | case CursorOpLast => "->|" 17 | case CursorOpUp => "_/" 18 | case CursorOpLeftN(n) => "-<-:(" + n + ")" 19 | case CursorOpRightN(n) => ":->-(" + n + ")" 20 | case CursorOpLeftAt(_) => "?<-:" 21 | case CursorOpRightAt(_) => ":->?" 22 | case CursorOpFind(_) => "find" 23 | case CursorOpField(f) => "--(" + f + ")" 24 | case CursorOpDownField(f) => "--\\(" + f + ")" 25 | case CursorOpDownArray => "\\\\" 26 | case CursorOpDownAt(_) => "-\\" 27 | case CursorOpDownN(n) => "=\\(" + n + ")" 28 | case CursorOpDeleteGoParent => "!_/" 29 | case CursorOpDeleteGoLeft => "<-!" 30 | case CursorOpDeleteGoRight => "!->" 31 | case CursorOpDeleteGoFirst => "|<-!" 32 | case CursorOpDeleteGoLast => "!->|" 33 | case CursorOpDeleteGoField(f) => "!--(" + f + ")" 34 | case CursorOpDeleteLefts => "!<" 35 | case CursorOpDeleteRights => ">!" 36 | case CursorOpSetLefts(_) => "!...<" 37 | case CursorOpSetRights(_) => ">...!" 38 | } 39 | } 40 | 41 | def equal(e1: CursorOpElement, e2: CursorOpElement) = { 42 | e1 == e2 43 | } 44 | } 45 | } 46 | 47 | def cursorOpLeftNL: CursorOpElement @?> Int = 48 | PLens { 49 | case CursorOpLeftN(n) => Some(Store(CursorOpLeftN(_), n)) 50 | case _ => None 51 | } 52 | 53 | def cursorOpRightNL: CursorOpElement @?> Int = 54 | PLens { 55 | case CursorOpRightN(n) => Some(Store(CursorOpRightN(_), n)) 56 | case _ => None 57 | } 58 | 59 | def cursorOpLeftAtL: CursorOpElement @?> (Json => Boolean) = 60 | PLens { 61 | case CursorOpLeftAt(p) => Some(Store(CursorOpLeftAt(_), p)) 62 | case _ => None 63 | } 64 | 65 | def cursorOpRightAtL: CursorOpElement @?> (Json => Boolean) = 66 | PLens { 67 | case CursorOpRightAt(p) => Some(Store(CursorOpRightAt(_), p)) 68 | case _ => None 69 | } 70 | 71 | def cursorOpFindL: CursorOpElement @?> (Json => Boolean) = 72 | PLens { 73 | case CursorOpFind(p) => Some(Store(CursorOpFind(_), p)) 74 | case _ => None 75 | } 76 | 77 | def cursorOpFieldL: CursorOpElement @?> JsonField = 78 | PLens { 79 | case CursorOpField(f) => Some(Store(CursorOpField(_), f)) 80 | case _ => None 81 | } 82 | 83 | def cursorOpDownFieldL: CursorOpElement @?> JsonField = 84 | PLens { 85 | case CursorOpDownField(f) => Some(Store(CursorOpDownField(_), f)) 86 | case _ => None 87 | } 88 | 89 | def cursorOpDownAtL: CursorOpElement @?> (Json => Boolean) = 90 | PLens { 91 | case CursorOpDownAt(p) => Some(Store(CursorOpDownAt(_), p)) 92 | case _ => None 93 | } 94 | 95 | def cursorOpDownNL: CursorOpElement @?> Int = 96 | PLens { 97 | case CursorOpDownN(n) => Some(Store(CursorOpDownN(_), n)) 98 | case _ => None 99 | } 100 | 101 | def cursorOpDeleteGoFieldL: CursorOpElement @?> JsonField = 102 | PLens { 103 | case CursorOpDeleteGoField(f) => Some(Store(CursorOpDeleteGoField(_), f)) 104 | case _ => None 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/CursorOpScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import syntax.show.* 5 | import CursorOpElementScalaz.* 6 | 7 | object CursorOpScalaz extends CursorOpScalazs {} 8 | 9 | trait CursorOpScalazs { 10 | implicit val CursorOpInstances: Show[CursorOp] & Equal[CursorOp] = { 11 | new Show[CursorOp] with Equal[CursorOp] { 12 | override def show(x: CursorOp) = x match { 13 | case Reattempt => 14 | Cord(".?.") 15 | case El(o, s) => 16 | if (s) o.show else Cord("*." + o.shows) 17 | } 18 | def equal(a1: CursorOp, a2: CursorOp) = { 19 | a1 == a2 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/CursorScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import syntax.show.* 5 | import syntax.equal.* 6 | import std.string.* 7 | import std.list.* 8 | import Json.* 9 | import JsonScalaz.* 10 | import JsonObjectScalaz.* 11 | import ContextScalaz.* 12 | 13 | object CursorScalaz extends CursorScalazs {} 14 | 15 | trait CursorScalazs { 16 | 17 | /** 18 | * A lens of a cursor's focus. 19 | */ 20 | val focusL: Cursor @> Json = 21 | Lens { 22 | case CJson(j) => 23 | Store(CJson(_), j) 24 | case CArray(p, _, l, j, r) => 25 | Store(CArray(p, true, l, _, r), j) 26 | case CObject(p, _, x, (f, j)) => 27 | Store(jj => CObject(p, true, x, (f, jj)), j) 28 | } 29 | 30 | /** 31 | * A partial lens of the lefts of a cursor at a JSON array. 32 | */ 33 | val leftsL: Cursor @?> JsonArray = 34 | PLens { 35 | case CArray(p, _, l, j, r) => 36 | Some(Store(CArray(p, true, _, j, r), l)) 37 | case _ => None 38 | } 39 | 40 | /** 41 | * A partial lens of the left of a cursor at a JSON array. 42 | */ 43 | val leftL: Cursor @?> Json = 44 | PLens { 45 | case CArray(p, _, l, j, r) => 46 | l match { 47 | case Nil => None 48 | case h :: t => Some(Store(q => CArray(p, true, q :: t, j, r), h)) 49 | } 50 | case _ => None 51 | } 52 | 53 | /** 54 | * A partial lens of the rights of a cursor at a JSON array. 55 | */ 56 | val rightsL: Cursor @?> JsonArray = 57 | PLens { 58 | case CArray(p, _, l, j, r) => 59 | Some(Store(CArray(p, true, _, j, r), l)) 60 | case _ => None 61 | } 62 | 63 | /** 64 | * A partial lens of the right of a cursor at a JSON array. 65 | */ 66 | val rightL: Cursor @?> Json = 67 | PLens { 68 | case CArray(p, _, l, j, r) => 69 | l match { 70 | case Nil => None 71 | case h :: t => Some(Store(q => CArray(p, true, q :: t, j, r), h)) 72 | } 73 | case _ => None 74 | } 75 | 76 | implicit val CursorInstances: Equal[Cursor] & Show[Cursor] = new Equal[Cursor] with Show[Cursor] { 77 | def equal(c1: Cursor, c2: Cursor) = { 78 | c1 match { 79 | case CJson(j1) => 80 | c2 match { 81 | case CJson(j2) => j1 === j2 82 | case _ => false 83 | } 84 | case CArray(p1, _, l1, j1, r1) => 85 | c2 match { 86 | case CArray(p2, _, l2, j2, r2) => p1 === p2 && l1 === l2 && j1 === j2 && r1 === r2 87 | case _ => false 88 | } 89 | case CObject(p1, _, x1, (f1, j1)) => 90 | c2 match { 91 | case CObject(p2, _, x2, (f2, j2)) => p1 === p2 && x1 === x2 && f1 === f2 && j1 === j2 92 | case _ => false 93 | } 94 | } 95 | } 96 | 97 | override def show(c: Cursor): Cord = { 98 | Cord(z"${c.context} ==> ${c.focus}") 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/DecodeJsonScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import std.string.* 5 | 6 | object DecodeJsonScalaz extends DecodeJsonScalazs {} 7 | 8 | trait DecodeJsonScalazs { 9 | implicit def MaybeDecodeJson[A](implicit e: DecodeJson[A]): DecodeJson[Maybe[A]] = { 10 | implicitly[DecodeJson[Option[A]]].map(Maybe.fromOption) 11 | } 12 | 13 | implicit def ScalazEitherDecodeJson[A, B](implicit ea: DecodeJson[A], eb: DecodeJson[B]): DecodeJson[A \/ B] = { 14 | implicitly[DecodeJson[Either[A, B]]].map(\/.fromEither(_)) 15 | } 16 | 17 | implicit def ValidationDecodeJson[A, B](implicit 18 | ea: DecodeJson[A], 19 | eb: DecodeJson[B] 20 | ): DecodeJson[Validation[A, B]] = { 21 | DecodeJson(a => { 22 | val l = (a --\ "Failure").success 23 | val r = (a --\ "Success").success 24 | (l, r) match { 25 | case (Some(c), None) => ea(c).map(Failure(_)) 26 | case (None, Some(c)) => eb(c).map(Success(_)) 27 | case _ => DecodeResult.fail("[A, B]Validation[A, B]", a.history) 28 | } 29 | }) 30 | } 31 | 32 | implicit def IMapDecodeJson[A: DecodeJson]: DecodeJson[String ==>> A] = { 33 | DecodeJson.MapDecodeJson[String, A].map(a => ==>>.fromList(a.toList)).setName("[A]==>>[String, A]") 34 | } 35 | 36 | implicit def IListDecodeJson[A: DecodeJson]: DecodeJson[IList[A]] = { 37 | implicitly[DecodeJson[List[A]]].map(IList.fromList).setName("[A]IList[A]") 38 | } 39 | 40 | implicit def DListDecodeJson[A: DecodeJson]: DecodeJson[DList[A]] = { 41 | implicitly[DecodeJson[List[A]]].map(DList.fromList(_)).setName("[A]DList[A]") 42 | } 43 | 44 | implicit def EphemeralStreamDecodeJson[A: DecodeJson]: DecodeJson[EphemeralStream[A]] = { 45 | implicitly[DecodeJson[List[A]]].map(list => EphemeralStream.apply(list*)).setName("[A]EphemeralStream[A]") 46 | } 47 | 48 | implicit def ISetDecodeJson[A: DecodeJson: Order]: DecodeJson[ISet[A]] = { 49 | implicitly[DecodeJson[List[A]]].map(ISet.fromList(_)).setName("[A]ISet[A]") 50 | } 51 | 52 | implicit def NonEmptyListDecodeJson[A: DecodeJson]: DecodeJson[NonEmptyList[A]] = { 53 | implicitly[DecodeJson[List[A]]].flatMap(l => 54 | DecodeJson[NonEmptyList[A]](c => 55 | std.list.toNel(l) match { 56 | case Maybe.Empty() => DecodeResult.fail("[A]NonEmptyList[A]", c.history) 57 | case Maybe.Just(n) => DecodeResult.ok(n) 58 | } 59 | ) 60 | ) setName "[A]NonEmptyList[A]" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/DecodeResultScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import Isomorphism.* 5 | import Scalaz.* 6 | import CursorHistoryScalaz.* 7 | 8 | object DecodeResultScalaz extends DecodeResultScalazs {} 9 | 10 | trait DecodeResultScalazs { 11 | @annotation.tailrec 12 | final def loop[A, X](d: DecodeResult[A], e: (String, CursorHistory) => X, f: A => X \/ DecodeResult[A]): X = { 13 | if (d.isError) { 14 | e(d.message.get, d.history.get) 15 | } else { 16 | f(d.value.get) match { 17 | case -\/(x) => x 18 | case \/-(a) => loop(a, e, f) 19 | } 20 | } 21 | } 22 | 23 | def failedResultL[A]: DecodeResult[A] @?> (String, CursorHistory) = 24 | PLens(_.result.fold(q => Some(Store(r => DecodeResult.failResult(r._1, r._2), q)), _ => None)) 25 | 26 | def failedResultMessageL[A]: DecodeResult[A] @?> String = 27 | ~Lens.firstLens compose failedResultL[A] 28 | 29 | def failedResultHistoryL[A]: DecodeResult[A] @?> CursorHistory = 30 | ~Lens.secondLens compose failedResultL[A] 31 | 32 | implicit def DecodeResultMonad: Monad[DecodeResult] & Traverse[DecodeResult] = new Monad[DecodeResult] 33 | with Traverse[DecodeResult] { 34 | def point[A](a: => A) = DecodeResult.ok(a) 35 | def bind[A, B](a: DecodeResult[A])(f: A => DecodeResult[B]) = a flatMap f 36 | override def map[A, B](a: DecodeResult[A])(f: A => B) = a map f 37 | def traverseImpl[G[_]: Applicative, A, B](fa: DecodeResult[A])(f: A => G[B]): G[DecodeResult[B]] = 38 | fa.fold( 39 | (s, h) => DecodeResult.fail[B](s, h).pure[G], 40 | a => f(a).map(DecodeResult.ok) 41 | ) 42 | } 43 | 44 | type DecodeEither[A] = Either[(String, CursorHistory), A] 45 | 46 | val decodeResultIsoFunctor: IsoFunctor[DecodeResult, DecodeEither] = 47 | new IsoFunctorTemplate[DecodeResult, DecodeEither] { 48 | def to[A](decodeResult: DecodeResult[A]) = decodeResult.result 49 | def from[A](either: DecodeEither[A]) = DecodeResult[A](either) 50 | } 51 | 52 | def decodeResultIsoSet[A]: IsoSet[DecodeResult[A], DecodeEither[A]] = new IsoSet[DecodeResult[A], DecodeEither[A]] { 53 | def to = decodeResultIsoFunctor.to[A] 54 | def from = decodeResultIsoFunctor.from[A] 55 | } 56 | 57 | implicit def DecodeResultEqual[A: Equal]: Equal[DecodeResult[A]] = 58 | new IsomorphismEqual[DecodeResult[A], DecodeEither[A]] { 59 | def G = eitherEqual[(String, CursorHistory), A] 60 | def iso = decodeResultIsoSet 61 | } 62 | 63 | implicit def DecodeResultShow[A: Show]: Show[DecodeResult[A]] = 64 | new IsomorphismShow[DecodeResult[A], DecodeEither[A]] { 65 | def G = (e: Either[(String, CursorHistory), A]) => { 66 | e match { 67 | case Left(l) => l.show 68 | case Right(r) => r.show 69 | } 70 | } 71 | def iso = decodeResultIsoSet 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/EncodeJsonScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import Json.* 5 | 6 | object EncodeJsonScalaz extends EncodeJsonScalazs {} 7 | 8 | trait EncodeJsonScalazs { 9 | def fromFoldable[F[_], A](implicit A: EncodeJson[A], F: Foldable[F]): EncodeJson[F[A]] = 10 | EncodeJson(fa => jArray(F.foldLeft(fa, Nil: List[Json])((list, a) => A.encode(a) :: list).reverse)) 11 | 12 | implicit def MaybeEncodeJson[A](implicit e: EncodeJson[A]): EncodeJson[Maybe[A]] = EncodeJson(_.cata(e(_), jNull)) 13 | 14 | implicit def DisjunctionEncodeJson[A, B](implicit ea: EncodeJson[A], eb: EncodeJson[B]): EncodeJson[A \/ B] = 15 | EncodeJson( 16 | _.fold( 17 | a => jSingleObject("Left", ea(a)), 18 | b => jSingleObject("Right", eb(b)) 19 | ) 20 | ) 21 | 22 | implicit def ValidationEncodeJson[E, A](implicit ea: EncodeJson[E], eb: EncodeJson[A]): EncodeJson[Validation[E, A]] = 23 | EncodeJson( 24 | _.fold( 25 | e => jSingleObject("Failure", ea(e)), 26 | a => jSingleObject("Success", eb(a)) 27 | ) 28 | ) 29 | 30 | implicit def IListEncodeJson[A: EncodeJson]: EncodeJson[IList[A]] = 31 | fromFoldable[IList, A] 32 | 33 | implicit def DListEncodeJson[A: EncodeJson]: EncodeJson[DList[A]] = 34 | fromFoldable[DList, A] 35 | 36 | implicit def EphemeralStreamEncodeJson[A: EncodeJson]: EncodeJson[EphemeralStream[A]] = 37 | fromFoldable[EphemeralStream, A] 38 | 39 | implicit def ISetEncodeJson[A: EncodeJson]: EncodeJson[ISet[A]] = 40 | fromFoldable[ISet, A] 41 | 42 | implicit def NonEmptyListEncodeJson[A: EncodeJson]: EncodeJson[NonEmptyList[A]] = 43 | fromFoldable[NonEmptyList, A] 44 | 45 | implicit def IMapEncodeJson[A, B](implicit A: EncodeJsonKey[A], B: EncodeJson[B]): EncodeJson[A ==>> B] = 46 | EncodeJson(x => 47 | jObjectAssocList( 48 | x.foldrWithKey(Nil: List[(String, Json)])((k, v, list) => (A.toJsonKey(k), B(v)) :: list) 49 | ) 50 | ) 51 | 52 | implicit val EncodeJsonContra: Contravariant[EncodeJson] = new Contravariant[EncodeJson] { 53 | def contramap[A, B](r: EncodeJson[A])(f: B => A) = r contramap f 54 | } 55 | 56 | implicit val EncodeJsonNumberInstance: Contravariant[EncodeJsonNumber] = new Contravariant[EncodeJsonNumber] { 57 | def contramap[A, B](r: EncodeJsonNumber[A])(f: B => A) = r contramap f 58 | } 59 | 60 | implicit val EncodePossibleJsonNumberInstance: Contravariant[EncodePossibleJsonNumber] = 61 | new Contravariant[EncodePossibleJsonNumber] { 62 | def contramap[A, B](r: EncodePossibleJsonNumber[A])(f: B => A) = r contramap f 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/HCursorScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | 5 | object HCursorScalaz extends HCursorScalazs 6 | 7 | trait HCursorScalazs { 8 | val hcursorL: HCursor @> Cursor = 9 | Lens(c => Store(HCursor(_, c.history), c.cursor)) 10 | 11 | val hcursorHistoryL: HCursor @> CursorHistory = 12 | Lens(c => Store(HCursor(c.cursor, _), c.history)) 13 | } 14 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/JsonIdentityScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object JsonIdentityScalaz extends JsonIdentityScalazs 4 | 5 | trait JsonIdentityScalazs {} 6 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/JsonNumberScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | 5 | object JsonNumberScalaz { 6 | implicit val JsonNumberEqual: Equal[JsonNumber] = 7 | _ == _ 8 | } 9 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/JsonObjectScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import scalaz.syntax.foldable.* 5 | import scalaz.syntax.functor.* 6 | import JsonObject.* 7 | import Json.* 8 | 9 | object JsonObjectScalaz extends JsonObjectScalazs { 10 | def from[F[_]: Foldable](f: F[(JsonField, Json)]): JsonObject = { 11 | f.foldLeft(empty) { case (acc, (k, v)) => acc + (k, v) } 12 | } 13 | } 14 | 15 | trait JsonObjectScalazs { 16 | 17 | /** 18 | * The lens to the JSON value. 19 | */ 20 | def jsonObjectL(f: JsonField): JsonObject @> Option[Json] = { 21 | Lens(jsonObject => 22 | Store( 23 | _ match { 24 | case None => jsonObject - f 25 | case Some(v) => jsonObject + (f, v) 26 | }, 27 | jsonObject(f) 28 | ) 29 | ) 30 | } 31 | 32 | /** 33 | * The partial lens to the JSON value. 34 | */ 35 | def jsonObjectPL(f: JsonField): JsonObject @?> Json = { 36 | PLens.somePLens compose ~jsonObjectL(f) 37 | } 38 | 39 | implicit val JsonObjectShow: Show[JsonObject] = Show.showFromToString[JsonObject] 40 | 41 | implicit val JsonObjectEqual: Equal[JsonObject] = Equal.equalA[JsonObject] 42 | 43 | def traverse[F[_]](o: JsonObject, f: Json => F[Json])(implicit FF: Applicative[F]): F[JsonObject] = { 44 | o.toList 45 | .foldLeft(FF.point(List[JsonAssoc]())) { case (acc, (k, v)) => 46 | FF.apply2(acc, f(v)) { (elems, newV) => 47 | (k, newV) :: elems 48 | } 49 | } 50 | .map(elems => JsonObject.fromIterable(elems.reverse)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/JsonScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import Scalaz.* 5 | import Json.* 6 | import JsonNumberScalaz.* 7 | import JsonObjectScalaz.* 8 | 9 | object JsonScalaz extends JsonScalazs {} 10 | 11 | trait JsonScalazs { 12 | 13 | /** 14 | * A partial lens for JSON boolean values. 15 | */ 16 | def jBoolPL: Json @?> Boolean = 17 | PLens(_.fold(None, z => Some(Store(JBool, z)), _ => None, _ => None, _ => None, _ => None)) 18 | 19 | /** 20 | * A partial lens for JSON number values. 21 | */ 22 | def jNumberPL: Json @?> JsonNumber = 23 | PLens(_.fold(None, _ => None, z => Some(Store(JNumber, z)), _ => None, _ => None, _ => None)) 24 | 25 | /** 26 | * A partial lens for JSON string values. 27 | */ 28 | def jStringPL: Json @?> JsonString = 29 | PLens(_.fold(None, _ => None, _ => None, z => Some(Store(JString, z)), _ => None, _ => None)) 30 | 31 | /** 32 | * A partial lens for JSON array values. 33 | */ 34 | def jArrayPL: Json @?> JsonArray = 35 | PLens(_.fold(None, _ => None, _ => None, _ => None, z => Some(Store(JArray, z)), _ => None)) 36 | 37 | /** 38 | * A partial lens for JSON object values. 39 | */ 40 | def jObjectPL: Json @?> JsonObject = 41 | PLens(_.fold(None, _ => None, _ => None, _ => None, _ => None, z => Some(Store(JObject, z)))) 42 | 43 | /** 44 | * A partial lens for element of JSON array. 45 | */ 46 | def jsonArrayPL(n: Int): JsonArray @?> Json = 47 | PLens(array => array lift n map (Store(array.updated(n, _), _))) 48 | 49 | implicit val JsonInstances: Equal[Json] & Show[Json] = { 50 | new Equal[Json] with Show[Json] { 51 | def equal(a1: Json, a2: Json) = { 52 | a1 match { 53 | case JNull => a2.isNull 54 | case JBool(b) => a2.bool exists (_ == b) 55 | case JNumber(n) => a2.number exists (_ === n) 56 | case JString(s) => a2.string exists (_ == s) 57 | case JArray(a) => a2.array exists (_ === a) 58 | case JObject(o) => a2.obj exists (_ === o) 59 | } 60 | } 61 | 62 | override def show(a: Json) = Show.showFromToString.show(a) 63 | } 64 | } 65 | 66 | /** 67 | * Decode `A` based on `HCursor => ValidationNel[String, A]` function. 68 | */ 69 | def asWithValidation[A](f: HCursor => ValidationNel[String, A]): DecodeJson[A] = 70 | (c: HCursor) => 71 | f(c) match { 72 | case Success(a) => DecodeResult.ok(a) 73 | case Failure(err) => DecodeResult.fail(err.shows, c.history) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/PrettyParamsScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | 5 | object PrettyParamsScalaz extends PrettyParamsScalazs 6 | 7 | trait PrettyParamsScalazs { 8 | implicit val prettyParamsEqual: Equal[PrettyParams] = Equal.equalA 9 | } 10 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/main/scala/argonaut/StringWrapScalaz.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object StringWrapScalaz extends StringWrapScalazs 4 | 5 | trait StringWrapScalazs {} 6 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/test/scala/argonaut/JsonObjectScalazSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Data.* 4 | import scalaz.* 5 | import Scalaz.* 6 | import JsonObjectScalaz.* 7 | 8 | object JsonObjectScalazSpecification extends ArgonautSpec { 9 | def is = s2""" 10 | JsonObjectScalaz 11 | shows ${prop((o: JsonObject) => o.shows === o.toString)} 12 | """ 13 | } 14 | -------------------------------------------------------------------------------- /argonaut-scalaz/shared/src/test/scala/argonaut/JsonScalazSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.JsonScalaz.* 4 | import org.scalacheck.* 5 | import scalaz.Scalaz.* 6 | import scalaz.* 7 | import Data.ArbitraryJson 8 | 9 | object JsonScalazSpecification extends ArgonautSpec { 10 | 11 | private implicit def NonEmptyListStrGen: Gen[NonEmptyList[String]] = for { 12 | head <- Gen.alphaNumStr 13 | tail <- Gen.listOf(Gen.alphaNumStr) 14 | res <- Gen.const(NonEmptyList.fromSeq(head, tail)) 15 | } yield res 16 | 17 | private implicit def NonEmptyListArb: Arbitrary[NonEmptyList[String]] = 18 | Arbitrary { NonEmptyListStrGen } 19 | 20 | private def FailedValidationGen[A]: Gen[NonEmptyList[String] => ValidationNel[String, A]] = 21 | Gen.const { Validation.failure(_) } 22 | 23 | private implicit def FailedValidationArb[A]: Arbitrary[NonEmptyList[String] => ValidationNel[String, A]] = 24 | Arbitrary(FailedValidationGen) 25 | 26 | private def alwaysFailsToDecodeProp: Prop = 27 | prop((o: Json, failures: NonEmptyList[String], failed: NonEmptyList[String] => ValidationNel[String, Int]) => { 28 | val decoder: DecodeJson[Int] = asWithValidation[Int](_ => failed(failures)) 29 | decoder.decode(o.hcursor) === DecodeResult.fail(failures.shows, CursorHistory.empty) 30 | }) 31 | 32 | private def alwaysDecodesSuccessfully: Prop = { 33 | prop((o: Json) => { 34 | val decoder: DecodeJson[Boolean] = asWithValidation(_ => true.successNel[String]) 35 | decoder.decode(o.hcursor) === DecodeResult.ok(true) 36 | }) 37 | } 38 | 39 | def is = s2""" 40 | JsonObjectScalaz 41 | asWithValidation with always failed validation ${alwaysFailsToDecodeProp} 42 | asWithValidation with always successful validation ${alwaysDecodesSuccessfully} 43 | """ 44 | 45 | } 46 | -------------------------------------------------------------------------------- /argonaut/js-native/src/test/scala-2/argonaut/ArgonautSpec.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.specs2.* 4 | import org.specs2.scalacheck.Parameters 5 | 6 | trait ArgonautSpec extends Specification with ScalaCheck { 7 | 8 | override implicit val defaultParameters: Parameters = 9 | Parameters(minTestsOk = 10, maxSize = 5) 10 | 11 | } 12 | -------------------------------------------------------------------------------- /argonaut/js-native/src/test/scala-3/argonaut/ArgonautSpec.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.specs2.* 4 | 5 | trait ArgonautSpec extends Specification with ScalaCheck { 6 | implicit final class MustEqualExtension[A](a1: A) { 7 | def must_==(a2: A) = a1 must beEqualTo(a2) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /argonaut/js/src/test/scala/argonaut/JsonFilesSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalajs.js 4 | import scalajs.js.Dynamic 5 | 6 | object JsonFilesSpecification extends JsonFilesSpecBase { 7 | val fs = Dynamic.global.require("fs") 8 | 9 | def listFiles(path: String): collection.Seq[String] = 10 | fs.readdirSync(path).asInstanceOf[js.Array[String]] 11 | 12 | override def testData = { 13 | listFiles(baseDir).map(path => 14 | TestData( 15 | fileName = path.split('/').last, 16 | jsonString = fs.readFileSync(baseDir + "/" + path).toString 17 | ) 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/browser-gumf.index.json: -------------------------------------------------------------------------------- 1 | { 2 | "new_experiments": [ 3 | {"default": 4 | {"name": "Dependencies", 5 | "jarfile": "base_classes.jar", 6 | "hash": "a89119c86198e5b3544fa50b2a570f79922cbee59d29a7716c69528d9267dbe5" 7 | } 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/browser-gumf.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Pepper Flash Player", 3 | "name": "Flapper", 4 | "version": "11.7.700.202", 5 | "x-flapper-revision": "1182653", 6 | "x-ppapi-arch": "ia32", 7 | "x-ppapi-os": "mac", 8 | "x-ppapi-required-interfaces": [ 9 | "PPB_AudioConfig;1.1|PPB_AudioConfig;1.0", 10 | "PPB_AudioInput(Dev);0.3|PPB_AudioInput(Dev);0.2", 11 | "PPB_Audio;1.0", 12 | "PPB_BrowserFont_Trusted;1.0", 13 | "PPB_Buffer(Dev);0.4", 14 | "PPB_CharSet(Dev);0.4", 15 | "PPB_Core;1.0", 16 | "PPB_Crypto(Dev);0.1", 17 | "PPB_CursorControl(Dev);0.4", 18 | "PPB_FileChooser(Dev);0.6|PPB_FileChooser(Dev);0.5", 19 | "PPB_FileChooserTrusted;0.6|PPB_FileChooserTrusted;0.5", 20 | "PPB_FileRef;1.0", 21 | "PPB_Flash_Clipboard;5.0|PPB_Flash_Clipboard;4.0", 22 | "PPB_Flash_File_FileRef;2", 23 | "PPB_Flash_File_ModuleLocal;3", 24 | "PPB_Flash_FontFile;0.1|PPB_PDF;1", 25 | "PPB_FlashFullscreen;1.0|PPB_FlashFullscreen;0.1", 26 | "PPB_Flash;13.0|PPB_Flash;12.6|PPB_Flash;12.5|PPB_Flash;12.4", 27 | "PPB_Flash_Menu;0.2", 28 | "PPB_Graphics2D;1.0", 29 | "PPB_Graphics3D;1.0", 30 | "PPB_ImageData;1.0", 31 | "PPB_IMEInputEvent(Dev);0.2|PPB_IMEInputEvent(Dev);0.1", 32 | "PPB_InputEvent;1.0", 33 | "PPB_Instance;1.0", 34 | "PPB_Memory(Dev);0.1", 35 | "PPB_NetAddress_Private;1.1|PPB_NetAddress_Private;1.0|PPB_NetAddress_Private;0.1", 36 | "PPB_OpenGLES2ChromiumMapSub;1.0|PPB_OpenGLES2ChromiumMapSub(Dev);1.0|PPB_GLESChromiumTextureMapping(Dev);0.1", 37 | "PPB_OpenGLES2;1.0", 38 | "PPB_TCPSocket_Private;0.4|PPB_TCPSocket_Private;0.3", 39 | "PPB_TextInput(Dev);0.2|PPB_TextInput(Dev);0.1", 40 | "PPB_UDPSocket_Private;0.4|PPB_UDPSocket_Private;0.3", 41 | "PPB_URLLoader;1.0", 42 | "PPB_URLLoaderTrusted;0.3", 43 | "PPB_URLRequestInfo;1.0", 44 | "PPB_URLResponseInfo;1.0", 45 | "PPB_URLUtil(Dev);0.6", 46 | "PPB_Var;1.1|PPB_Var;1.0", 47 | "PPB_VideoCapture(Dev);0.3|PPB_VideoCapture(Dev);0.2", 48 | "PPB_View;1.0" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/browser-gumf.messages0.json: -------------------------------------------------------------------------------- 1 | { 2 | "appDesc": { 3 | "description": "App description.", 4 | "message": "\u5783\u573E\u90F5\u4EF6\u66F4\u5C11\u3001\u8FC5\u901F\uFF0C\u4E26\u4E14\u53EF\u4F9B\u641C\u5C0B\u7684\u96FB\u5B50\u90F5\u4EF6\u670D\u52D9\u3002" 5 | }, 6 | "appName": { 7 | "description": "App name.", 8 | "message": "Gmail" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/browser-gumf.messages1.json: -------------------------------------------------------------------------------- 1 | { 2 | "appDesc": { 3 | "description": "App description.", 4 | "message": "Fast, searchable email with less spam." 5 | }, 6 | "appName": { 7 | "description": "App name.", 8 | "message": "Gmail" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/browser-gumf.messages2.json: -------------------------------------------------------------------------------- 1 | { 2 | "appDesc": { 3 | "description": "App description.", 4 | "message": "Google Drive: t\u1EA1o, chia s\u1EBB v\u00E0 l\u01B0u gi\u1EEF t\u1EA5t c\u1EA3 n\u1ED9i dung c\u1EE7a b\u1EA1n \u1EDF m\u1ED9t n\u01A1i." 5 | }, 6 | "appName": { 7 | "description": "App name.", 8 | "message": "Google Drive" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/browser-gumf.messages3.json: -------------------------------------------------------------------------------- 1 | { 2 | "appDesc": { 3 | "description": "App description.", 4 | "message": "Vytv\u00E1\u0159ejte a upravujte dokumenty" 5 | }, 6 | "appName": { 7 | "description": "App name.", 8 | "message": "Dokumenty Google" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/browser-gumf.webapps.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/example.json: -------------------------------------------------------------------------------- 1 | {"web-app": { 2 | "servlet": [ 3 | { 4 | "servlet-name": "cofaxCDS", 5 | "servlet-class": "org.cofax.cds.CDSServlet", 6 | "init-param": { 7 | "configGlossary:installationAt": "Philadelphia, PA", 8 | "configGlossary:adminEmail": "ksm@pobox.com", 9 | "configGlossary:poweredBy": "Cofax", 10 | "configGlossary:poweredByIcon": "/images/cofax.gif", 11 | "configGlossary:staticPath": "/content/static", 12 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 13 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 14 | "templatePath": "templates", 15 | "templateOverridePath": "", 16 | "defaultListTemplate": "listTemplate.htm", 17 | "defaultFileTemplate": "articleTemplate.htm", 18 | "useJSP": false, 19 | "jspListTemplate": "listTemplate.jsp", 20 | "jspFileTemplate": "articleTemplate.jsp", 21 | "cachePackageTagsTrack": 200, 22 | "cachePackageTagsStore": 200, 23 | "cachePackageTagsRefresh": 60, 24 | "cacheTemplatesTrack": 100, 25 | "cacheTemplatesStore": 50, 26 | "cacheTemplatesRefresh": 15, 27 | "cachePagesTrack": 200, 28 | "cachePagesStore": 100, 29 | "cachePagesRefresh": 10, 30 | "cachePagesDirtyRead": 10, 31 | "searchEngineListTemplate": "forSearchEnginesList.htm", 32 | "searchEngineFileTemplate": "forSearchEngines.htm", 33 | "searchEngineRobotsDb": "WEB-INF/robots.db", 34 | "useDataStore": true, 35 | "dataStoreClass": "org.cofax.SqlDataStore", 36 | "redirectionClass": "org.cofax.SqlRedirection", 37 | "dataStoreName": "cofax", 38 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 39 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 40 | "dataStoreUser": "sa", 41 | "dataStorePassword": "dataStoreTestQuery", 42 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 43 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 44 | "dataStoreInitConns": 10, 45 | "dataStoreMaxConns": 100, 46 | "dataStoreConnUsageLimit": 100, 47 | "dataStoreLogLevel": "debug", 48 | "maxUrlLength": 500}}, 49 | { 50 | "servlet-name": "cofaxEmail", 51 | "servlet-class": "org.cofax.cds.EmailServlet", 52 | "init-param": { 53 | "mailHost": "mail1", 54 | "mailHostOverride": "mail2"}}, 55 | { 56 | "servlet-name": "cofaxAdmin", 57 | "servlet-class": "org.cofax.cds.AdminServlet"}, 58 | 59 | { 60 | "servlet-name": "fileServlet", 61 | "servlet-class": "org.cofax.cds.FileServlet"}, 62 | { 63 | "servlet-name": "cofaxTools", 64 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 65 | "init-param": { 66 | "templatePath": "toolstemplates/", 67 | "log": 1, 68 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 69 | "logMaxSize": "", 70 | "dataLog": 1, 71 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 72 | "dataLogMaxSize": "", 73 | "removePageCache": "/content/admin/remove?cache=pages&id=", 74 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 75 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 76 | "lookInContext": 1, 77 | "adminGroupID": 4, 78 | "betaServer": true}}], 79 | "servlet-mapping": { 80 | "cofaxCDS": "/", 81 | "cofaxEmail": "/cofaxutil/aemail/*", 82 | "cofaxAdmin": "/admin/*", 83 | "fileServlet": "/static/*", 84 | "cofaxTools": "/tools/*"}, 85 | 86 | "taglib": { 87 | "taglib-uri": "cofax.tld", 88 | "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} 89 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/travis.argonaut.json: -------------------------------------------------------------------------------- 1 | {"id":7763591,"repository_id":165099,"number":"231","config":{"language":"scala","scala":["2.9.2","2.9.3","2.10.0","2.10.1"],"jdk":["oraclejdk7","openjdk6","openjdk7"],".result":"configured"},"state":"finished","result":1,"status":1,"started_at":"2013-06-04T09:46:22Z","finished_at":"2013-06-04T10:02:02Z","duration":2845,"commit":"349c3a3c59f2c99a1219979d760b11beac429211","branch":"master","message":"Tweak to improve printing performance.","committed_at":"2013-06-04T09:36:42Z","author_name":"Sean Parsons","author_email":"github@futurenotfound.com","committer_name":"markhibberd","committer_email":"mark@hibberd.id.au","compare_url":"https://github.com/markhibberd/argonaut/compare/31ae4b8ceed0...349c3a3c59f2","event_type":"push","matrix":[{"id":7763592,"repository_id":165099,"number":"231.1","config":{"language":"scala","scala":"2.9.2","jdk":"oraclejdk7",".result":"configured"},"result":0,"started_at":"2013-06-04T09:46:22Z","finished_at":"2013-06-04T09:51:16Z","allow_failure":false},{"id":7763593,"repository_id":165099,"number":"231.2","config":{"language":"scala","scala":"2.9.2","jdk":"openjdk6",".result":"configured"},"result":0,"started_at":"2013-06-04T09:51:23Z","finished_at":"2013-06-04T09:55:10Z","allow_failure":false},{"id":7763594,"repository_id":165099,"number":"231.3","config":{"language":"scala","scala":"2.9.2","jdk":"openjdk7",".result":"configured"},"result":0,"started_at":"2013-06-04T09:47:05Z","finished_at":"2013-06-04T09:50:55Z","allow_failure":false},{"id":7763595,"repository_id":165099,"number":"231.4","config":{"language":"scala","scala":"2.9.3","jdk":"oraclejdk7",".result":"configured"},"result":0,"started_at":"2013-06-04T09:49:18Z","finished_at":"2013-06-04T09:53:30Z","allow_failure":false},{"id":7763596,"repository_id":165099,"number":"231.5","config":{"language":"scala","scala":"2.9.3","jdk":"openjdk6",".result":"configured"},"result":0,"started_at":"2013-06-04T09:51:31Z","finished_at":"2013-06-04T09:54:51Z","allow_failure":false},{"id":7763597,"repository_id":165099,"number":"231.6","config":{"language":"scala","scala":"2.9.3","jdk":"openjdk7",".result":"configured"},"result":0,"started_at":"2013-06-04T09:52:11Z","finished_at":"2013-06-04T09:56:59Z","allow_failure":false},{"id":7763598,"repository_id":165099,"number":"231.7","config":{"language":"scala","scala":"2.10.0","jdk":"oraclejdk7",".result":"configured"},"result":1,"started_at":"2013-06-04T09:54:18Z","finished_at":"2013-06-04T09:58:22Z","allow_failure":false},{"id":7763599,"repository_id":165099,"number":"231.8","config":{"language":"scala","scala":"2.10.0","jdk":"openjdk6",".result":"configured"},"result":0,"started_at":"2013-06-04T09:54:03Z","finished_at":"2013-06-04T09:57:22Z","allow_failure":false},{"id":7763600,"repository_id":165099,"number":"231.9","config":{"language":"scala","scala":"2.10.0","jdk":"openjdk7",".result":"configured"},"result":0,"started_at":"2013-06-04T09:55:37Z","finished_at":"2013-06-04T09:59:26Z","allow_failure":false},{"id":7763601,"repository_id":165099,"number":"231.10","config":{"language":"scala","scala":"2.10.1","jdk":"oraclejdk7",".result":"configured"},"result":0,"started_at":"2013-06-04T09:55:51Z","finished_at":"2013-06-04T09:59:59Z","allow_failure":false},{"id":7763602,"repository_id":165099,"number":"231.11","config":{"language":"scala","scala":"2.10.1","jdk":"openjdk6",".result":"configured"},"result":1,"started_at":"2013-06-04T09:58:33Z","finished_at":"2013-06-04T10:02:02Z","allow_failure":false},{"id":7763603,"repository_id":165099,"number":"231.12","config":{"language":"scala","scala":"2.10.1","jdk":"openjdk7",".result":"configured"},"result":0,"started_at":"2013-06-04T09:57:57Z","finished_at":"2013-06-04T10:01:42Z","allow_failure":false}]} -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/travis.stats.json: -------------------------------------------------------------------------------- 1 | {"stats":{"params":{},"current_user":null}} -------------------------------------------------------------------------------- /argonaut/jvm/src/test/resources/data/twitter1.json: -------------------------------------------------------------------------------- 1 | {"results":[{"from_user_id_str":"80430860","profile_image_url":"http://a2.twimg.com/profile_images/536455139/icon32_normal.png","created_at":"Wed, 26 Jan 2011 07:07:02 +0000","from_user":"kazu_yamamoto","id_str":"30159761706061824","metadata":{"result_type":"recent"},"to_user_id":null,"text":"Haskell Server Pages \u3063\u3066\u3001\u307e\u3060\u7d9a\u3044\u3066\u3044\u305f\u306e\u304b\uff01","id":30159761706061824,"from_user_id":80430860,"geo":null,"iso_language_code":"no","to_user_id_str":null,"source":"<a href="http://twitter.com/">web</a>"}],"max_id":30159761706061824,"since_id":0,"refresh_url":"?since_id=30159761706061824&q=haskell","next_page":"?page=2&max_id=30159761706061824&rpp=1&q=haskell","results_per_page":1,"page":1,"completed_in":0.012606,"since_id_str":"0","max_id_str":"30159761706061824","query":"haskell"} -------------------------------------------------------------------------------- /argonaut/jvm/src/test/scala/argonaut/ArgonautSpec.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.specs2.* 4 | 5 | trait ArgonautSpec extends Specification with ScalaCheck { 6 | 7 | implicit final class MustEqualExtension[A](a1: A) { 8 | def must_==(a2: A) = a1 must beEqualTo(a2) 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/scala/argonaut/JsonFilesSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.JsonFilesSpecBase.TestData 4 | import java.io.File 5 | import scala.io.Source 6 | 7 | object JsonFilesSpecification extends JsonFilesSpecBase { 8 | override def testData: Seq[TestData] = { 9 | val files = new File(baseDir).listFiles.toList 10 | files.map { file => 11 | TestData( 12 | fileName = file.getName, 13 | jsonString = Source.fromFile(file).mkString 14 | ) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /argonaut/jvm/src/test/scala/argonaut/PrettyParamsJVMSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.scalacheck.Prop.* 4 | import org.scalacheck.Gen 5 | import scala.concurrent.Await 6 | import scala.concurrent.Future 7 | import scala.concurrent.duration.* 8 | import scala.concurrent.ExecutionContext.Implicits.global 9 | 10 | object PrettyParamsJVMSpecification extends ArgonautSpec { 11 | def is = s2""" 12 | Parallelisation Safety 13 | vectorMemo ${testMemo(PrettyParams.vectorMemo)} 14 | """ 15 | 16 | def testMemo(memoFunction: (Int => String) => (Int => String)) = forAllNoShrink(Gen.choose(0, 100)) { count => 17 | val f: Int => String = _.toString 18 | val memo = memoFunction(f) 19 | val notParallel = (1 to count).map(f).toList 20 | val isParallel = Await 21 | .result( 22 | Future.traverse(1 to count)(n => Future(memo(n))), 23 | 5.seconds 24 | ) 25 | .toList 26 | isParallel must beEqualTo(notParallel) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala-2/argonaut/CodecJsonMacro.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scala.language.experimental.macros 4 | 5 | trait CodecJsonMacro { self: CodecJson.type => 6 | def derive[A]: CodecJson[A] = macro internal.Macros.materializeCodecImpl[A] 7 | } 8 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala-2/argonaut/DecodeJsonMacro.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scala.language.experimental.macros 4 | 5 | trait DecodeJsonMacro { self: DecodeJson.type => 6 | def derive[A]: DecodeJson[A] = macro internal.Macros.materializeDecodeImpl[A] 7 | } 8 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala-2/argonaut/EncodeJsonMacro.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scala.language.experimental.macros 4 | 5 | trait EncodeJsonMacro { self: EncodeJson.type => 6 | def derive[A]: EncodeJson[A] = macro internal.Macros.materializeEncodeImpl[A] 7 | } 8 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala-2/argonaut/internal/Macros.scala: -------------------------------------------------------------------------------- 1 | package argonaut.internal 2 | 3 | import argonaut.* 4 | 5 | object Macros extends MacrosCompat { 6 | def materializeCodecImpl[T: c.WeakTypeTag](c: Context): c.Expr[CodecJson[T]] = { 7 | import c.universe.* 8 | val tpe = weakTypeOf[T] 9 | val encode = materializeEncodeImpl[T](c) 10 | val decode = materializeDecodeImpl[T](c) 11 | c.Expr[CodecJson[T]](q""" 12 | _root_.argonaut.CodecJson.derived[$tpe]($encode, $decode) 13 | """) 14 | } 15 | 16 | def materializeEncodeImpl[T: c.WeakTypeTag](c: Context): c.Expr[EncodeJson[T]] = { 17 | import c.universe.* 18 | val tpe = weakTypeOf[T] 19 | 20 | val primaryConstructor = getDeclarations(c)(tpe).collectFirst { 21 | case m: MethodSymbol if m.isPrimaryConstructor => m 22 | } 23 | primaryConstructor match { 24 | case Some(constructor) => { 25 | val fieldNames: List[c.universe.Name] = getParameterLists(c)(constructor).flatten.map { field => 26 | field.name 27 | } 28 | val decodedNames: List[String] = fieldNames.map(_.decodedName.toString) 29 | val fieldTypes: List[c.universe.Type] = getParameterLists(c)(constructor).flatten.map { field => 30 | getDeclaration(c)(tpe, field.name).infoIn(tpe) 31 | } 32 | val fieldCount = fieldNames.size 33 | val invocations = fieldNames.map { fieldName => 34 | val termName = createTermName(c)(fieldName.toString) 35 | q"toEncode.$termName" 36 | } 37 | val methodName = createTermName(c)(s"jencode${fieldCount}L") 38 | val expr = c.Expr[EncodeJson[T]] { 39 | q""" 40 | _root_.argonaut.EncodeJson.$methodName[$tpe, ..$fieldTypes](toEncode => (..$invocations))(..$decodedNames) 41 | """ 42 | } 43 | // println(expr) 44 | expr 45 | } 46 | case None => c.abort(c.enclosingPosition, "Could not identify primary constructor for " + tpe) 47 | } 48 | } 49 | 50 | def materializeDecodeImpl[T: c.WeakTypeTag](c: Context): c.Expr[DecodeJson[T]] = { 51 | import c.universe.* 52 | val tpe = weakTypeOf[T] 53 | 54 | val primaryConstructor = getDeclarations(c)(tpe).collectFirst { 55 | case m: MethodSymbol if m.isPrimaryConstructor => m 56 | } 57 | primaryConstructor match { 58 | case Some(constructor) => { 59 | val fieldNames: List[c.universe.Name] = getParameterLists(c)(constructor).flatten.map { field => 60 | field.name 61 | } 62 | val decodedNames: List[String] = fieldNames.map(_.decodedName.toString) 63 | val fieldTypes: List[c.universe.Type] = getParameterLists(c)(constructor).flatten.map { field => 64 | getDeclaration(c)(tpe, field.name).infoIn(tpe) 65 | } 66 | val fieldCount = fieldNames.size 67 | val functionParameters = fieldNames.zip(fieldTypes).map { case (fieldName, fieldType) => 68 | val termName = createTermName(c)(fieldName.toString) 69 | q"$termName: $fieldType" 70 | } 71 | val parameters = fieldNames.map { fieldName => 72 | val termName = createTermName(c)(fieldName.toString) 73 | q"$termName" 74 | } 75 | val methodName = createTermName(c)("jdecode" + fieldCount.toString + "L") 76 | val expr = c.Expr[DecodeJson[T]] { 77 | q""" 78 | _root_.argonaut.DecodeJson.$methodName[..$fieldTypes, $tpe]((..$functionParameters) => new $tpe(..$parameters))(..$decodedNames) 79 | """ 80 | } 81 | // println(expr) 82 | expr 83 | } 84 | case None => c.abort(c.enclosingPosition, "Could not identify primary constructor for " + tpe) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala-2/argonaut/internal/MacrosCompat.scala: -------------------------------------------------------------------------------- 1 | package argonaut.internal 2 | 3 | trait MacrosCompat { 4 | type Context = scala.reflect.macros.blackbox.Context 5 | 6 | def getDeclarations(c: Context)(tpe: c.universe.Type): c.universe.MemberScope = tpe.decls 7 | 8 | def getParameterLists(c: Context)(method: c.universe.MethodSymbol): List[List[c.universe.Symbol]] = method.paramLists 9 | 10 | def getDeclaration(c: Context)(tpe: c.universe.Type, name: c.universe.Name): c.universe.Symbol = tpe.decl(name) 11 | 12 | def createTermName(c: Context)(name: String): c.universe.TermName = c.universe.TermName(name) 13 | } 14 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala-3/argonaut/CodecJsonMacro.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scala.deriving.Mirror 4 | 5 | trait CodecJsonMacro { self: CodecJson.type => 6 | inline def derive[A: Mirror.ProductOf]: CodecJson[A] = 7 | internal.Macros.derivedCodec[A] 8 | } 9 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala-3/argonaut/DecodeJsonMacro.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scala.deriving.Mirror 4 | 5 | trait DecodeJsonMacro { self: DecodeJson.type => 6 | inline def derive[A](using Mirror.ProductOf[A]): DecodeJson[A] = 7 | internal.Macros.derivedDecoder[A] 8 | } 9 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala-3/argonaut/EncodeJsonMacro.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scala.deriving.Mirror 4 | 5 | trait EncodeJsonMacro { self: EncodeJson.type => 6 | inline def derive[A](using Mirror.ProductOf[A]): EncodeJson[A] = 7 | internal.Macros.derivedEncoder[A] 8 | } 9 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/Argonaut.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | object Argonaut extends Argonauts 4 | 5 | trait Argonauts 6 | extends ACursors 7 | with CodecJsons 8 | with Contexts 9 | with ContextElements 10 | with Cursors 11 | with CursorHistorys 12 | with CursorOps 13 | with CursorOpElements 14 | with DecodeJsons 15 | with DecodeResults 16 | with EncodeJsons 17 | with HCursors 18 | with Jsons 19 | with JsonIdentitys 20 | with JsonObjects 21 | with PrettyParamss 22 | with StringWraps 23 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/CodecJson.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | sealed abstract class CodecJson[A] extends EncodeJson[A] with DecodeJson[A] { outer => 4 | val Encoder: EncodeJson[A] 5 | val Decoder: DecodeJson[A] 6 | 7 | override def encode(a: A) = Encoder.encode(a) 8 | override def decode(c: HCursor) = Decoder.decode(c) 9 | override def setName(n: String): CodecJson[A] = { 10 | new CodecJson[A] { 11 | val Encoder = outer.Encoder 12 | val Decoder = outer.Decoder.setName(n) 13 | } 14 | } 15 | 16 | override def tryDecode(c: ACursor) = Decoder.tryDecode(c) 17 | 18 | def xmap[B](f: A => B)(g: B => A): CodecJson[B] = { 19 | CodecJson.derived(using Encoder.contramap(g), Decoder.map(f)) 20 | } 21 | } 22 | 23 | object CodecJson extends CodecJsons with CodecJsonMacro { 24 | def apply[A](encoder: A => Json, decoder: HCursor => DecodeResult[A]): CodecJson[A] = 25 | derived(using EncodeJson(encoder), DecodeJson(decoder)) 26 | 27 | def withReattempt[A](encoder: A => Json, decoder: ACursor => DecodeResult[A]): CodecJson[A] = 28 | derived(using EncodeJson(encoder), DecodeJson.withReattempt(decoder)) 29 | 30 | def derived[A](implicit E: EncodeJson[A], D: DecodeJson[A]): CodecJson[A] = { 31 | new CodecJson[A] { 32 | val Encoder = E 33 | val Decoder = D 34 | } 35 | } 36 | 37 | def codecLaw[A](codec: CodecJson[A])(a: A): Boolean = { 38 | codec.decodeJson(codec.encode(a)).value.exists(_ == a) 39 | } 40 | } 41 | 42 | trait CodecJsons extends GeneratedCodecJsons 43 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/Context.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Json.* 4 | 5 | sealed abstract class Context { 6 | val toList: List[ContextElement] 7 | 8 | def +:(e: ContextElement): Context = 9 | Context.build(e :: toList) 10 | } 11 | 12 | object Context extends Contexts { 13 | def empty: Context = 14 | new Context { 15 | val toList: List[ContextElement] = Nil 16 | } 17 | } 18 | 19 | trait Contexts { 20 | private[argonaut] def build(x: List[ContextElement]): Context = 21 | new Context { 22 | val toList = x 23 | } 24 | } 25 | 26 | sealed abstract class ContextElement extends Product with Serializable { 27 | def json: Json = 28 | this match { 29 | case ArrayContext(_, j) => j 30 | case ObjectContext(_, j) => j 31 | } 32 | 33 | def field: Option[JsonField] = 34 | this match { 35 | case ArrayContext(_, _) => None 36 | case ObjectContext(f, _) => Some(f) 37 | } 38 | 39 | def index: Option[Int] = 40 | this match { 41 | case ArrayContext(n, _) => Some(n) 42 | case ObjectContext(_, _) => None 43 | } 44 | } 45 | private case class ArrayContext(n: Int, j: Json) extends ContextElement 46 | private case class ObjectContext(f: JsonField, j: Json) extends ContextElement 47 | 48 | object ContextElement extends ContextElements 49 | 50 | trait ContextElements { 51 | def arrayContext(n: Int, j: Json): ContextElement = 52 | ArrayContext(n, j) 53 | 54 | def objectContext(f: JsonField, j: Json): ContextElement = 55 | ObjectContext(f, j) 56 | } 57 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/CursorHistory.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | /** 4 | * A list of elements denoting the history of a cursor. 5 | * 6 | * Note: Most recent operation appears at head of list. 7 | * 8 | * @author Tony Morris 9 | */ 10 | case class CursorHistory(toList: List[CursorOp]) { 11 | def head: Option[CursorOp] = 12 | toList.headOption 13 | 14 | /** 15 | * Append two lists of cursor history. 16 | */ 17 | def ++(h: CursorHistory): CursorHistory = 18 | CursorHistory(toList ++ h.toList) 19 | 20 | def acursorElement(f: Cursor => Option[Cursor], c: Cursor, e: CursorOpElement): ACursor = { 21 | f(c) match { 22 | case None => +:(CursorOp.failedOp(e)).failedACursor(c) 23 | case Some(q) => +:(CursorOp(e)).acursor(q) 24 | } 25 | } 26 | 27 | /** 28 | * Prepend a cursor operation to the history. 29 | */ 30 | def +:(o: CursorOp): CursorHistory = 31 | CursorHistory(o +: toList) 32 | 33 | def failedACursor(c: Cursor): ACursor = 34 | ACursor.fail(HCursor(c, this)) 35 | 36 | def acursor(c: Cursor): ACursor = 37 | ACursor.ok(HCursor(c, this)) 38 | 39 | override def toString(): String = s"CursorHistory(${toList.toString})" 40 | } 41 | 42 | object CursorHistory extends CursorHistorys 43 | 44 | trait CursorHistorys { 45 | def empty: CursorHistory = CursorHistory(List()) 46 | 47 | def start(e: CursorOp): CursorHistory = CursorHistory(List(e)) 48 | } 49 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/CursorOp.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | sealed abstract class CursorOp extends Product with Serializable { 4 | def isReattempt: Boolean = 5 | this == Reattempt 6 | 7 | def isNotReattempt: Boolean = 8 | this != Reattempt 9 | 10 | def succeeded: Boolean = 11 | this match { 12 | case Reattempt => false 13 | case El(_, s) => s 14 | } 15 | 16 | def failed: Boolean = 17 | this match { 18 | case Reattempt => false 19 | case El(_, s) => !s 20 | } 21 | } 22 | 23 | case object Reattempt extends CursorOp 24 | case class El(o: CursorOpElement, success: Boolean) extends CursorOp 25 | 26 | object CursorOp extends CursorOps { 27 | def apply(o: CursorOpElement): CursorOp = 28 | El(o, true) 29 | } 30 | 31 | trait CursorOps { 32 | def reattemptOp: CursorOp = 33 | Reattempt 34 | 35 | def failedOp(o: CursorOpElement): CursorOp = 36 | El(o, false) 37 | } 38 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/CursorOpElement.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Json.* 4 | 5 | sealed abstract class CursorOpElement extends Product with Serializable { 6 | def isLeft: Boolean = 7 | this == CursorOpLeft 8 | 9 | def isRight: Boolean = 10 | this == CursorOpRight 11 | 12 | def isFirst: Boolean = 13 | this == CursorOpFirst 14 | 15 | def isLast: Boolean = 16 | this == CursorOpLast 17 | 18 | def isUp: Boolean = 19 | this == CursorOpUp 20 | 21 | def isDeleteGoParent: Boolean = 22 | this == CursorOpDeleteGoParent 23 | 24 | def isDeleteGoLeft: Boolean = 25 | this == CursorOpDeleteGoLeft 26 | 27 | def isDeleteGoRight: Boolean = 28 | this == CursorOpDeleteGoRight 29 | 30 | def isDeleteGoFirst: Boolean = 31 | this == CursorOpDeleteGoFirst 32 | 33 | def isDeleteGoLast: Boolean = 34 | this == CursorOpDeleteGoLast 35 | 36 | def isDeleteLefts: Boolean = 37 | this == CursorOpDeleteLefts 38 | 39 | def isDeleteRights: Boolean = 40 | this == CursorOpDeleteRights 41 | 42 | } 43 | case object CursorOpLeft extends CursorOpElement 44 | case object CursorOpRight extends CursorOpElement 45 | case object CursorOpFirst extends CursorOpElement 46 | case object CursorOpLast extends CursorOpElement 47 | case object CursorOpUp extends CursorOpElement 48 | case class CursorOpLeftN(n: Int) extends CursorOpElement 49 | case class CursorOpRightN(n: Int) extends CursorOpElement 50 | case class CursorOpLeftAt(p: Json => Boolean) extends CursorOpElement 51 | case class CursorOpRightAt(p: Json => Boolean) extends CursorOpElement 52 | case class CursorOpFind(p: Json => Boolean) extends CursorOpElement 53 | case class CursorOpField(f: JsonField) extends CursorOpElement 54 | case class CursorOpDownField(f: JsonField) extends CursorOpElement 55 | case object CursorOpDownArray extends CursorOpElement 56 | case class CursorOpDownAt(p: Json => Boolean) extends CursorOpElement 57 | case class CursorOpDownN(n: Int) extends CursorOpElement 58 | case object CursorOpDeleteGoParent extends CursorOpElement 59 | case object CursorOpDeleteGoLeft extends CursorOpElement 60 | case object CursorOpDeleteGoRight extends CursorOpElement 61 | case object CursorOpDeleteGoFirst extends CursorOpElement 62 | case object CursorOpDeleteGoLast extends CursorOpElement 63 | case class CursorOpDeleteGoField(f: JsonField) extends CursorOpElement 64 | case object CursorOpDeleteLefts extends CursorOpElement 65 | case object CursorOpDeleteRights extends CursorOpElement 66 | case class CursorOpSetLefts(x: List[Json]) extends CursorOpElement 67 | case class CursorOpSetRights(x: List[Json]) extends CursorOpElement 68 | 69 | object CursorOpElement extends CursorOpElements 70 | 71 | trait CursorOpElements {} 72 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/DecodeResult.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | case class DecodeResult[A](result: Either[(String, CursorHistory), A]) { 4 | def fold[X]( 5 | failure: (String, CursorHistory) => X, 6 | value: A => X 7 | ): X = result.fold({ case (m, h) => failure(m, h) }, value) 8 | 9 | final def loop[X, B](e: (String, CursorHistory) => X, f: B => Either[X, DecodeResult[B]])(implicit ev: A <:< B): X = 10 | DecodeResult.loop(this.map(ev), e, f) 11 | 12 | def isError: Boolean = 13 | result.isLeft 14 | 15 | def map[B](f: A => B): DecodeResult[B] = 16 | DecodeResult(result.map(f)) 17 | 18 | def flatMap[B](f: A => DecodeResult[B]): DecodeResult[B] = 19 | DecodeResult(result.flatMap(f(_).result)) 20 | 21 | def message: Option[String] = 22 | failure.map(_._1) 23 | 24 | def history: Option[CursorHistory] = 25 | failure.map(_._2) 26 | 27 | def toOption: Option[A] = 28 | result.toOption 29 | 30 | def toEither: Either[(String, CursorHistory), A] = 31 | result 32 | 33 | def getOr[AA >: A](els: => AA): AA = 34 | toOption.getOrElse(els) 35 | 36 | /** alias for `toOption` */ 37 | def value: Option[A] = 38 | result.toOption 39 | 40 | def failure: Option[(String, CursorHistory)] = 41 | result.left.toOption 42 | 43 | def option: DecodeResult[Option[A]] = 44 | result match { 45 | case Left((s, h)) => DecodeResult.fail(s, h) 46 | case Right(a) => DecodeResult.ok(Some(a)) 47 | } 48 | 49 | def |||[AA >: A](r: => DecodeResult[AA]): DecodeResult[AA] = 50 | DecodeResult(result.fold(_ => r.result, _ => result)) 51 | 52 | override def toString(): String = s"DecodeResult(${result})" 53 | } 54 | 55 | object DecodeResult extends DecodeResults { 56 | def ok[A](value: A): DecodeResult[A] = 57 | DecodeResult(Right(value)) 58 | 59 | def fail[A](s: String, h: CursorHistory): DecodeResult[A] = 60 | DecodeResult(Left((s, h))) 61 | } 62 | 63 | trait DecodeResults { 64 | type DecodeEither[A] = Either[(String, CursorHistory), A] 65 | 66 | def okResult[A](value: A): DecodeResult[A] = 67 | DecodeResult.ok(value) 68 | 69 | def failResult[A](s: String, h: CursorHistory): DecodeResult[A] = 70 | DecodeResult.fail(s, h) 71 | 72 | @annotation.tailrec 73 | final def loop[A, X](d: DecodeResult[A], e: (String, CursorHistory) => X, f: A => Either[X, DecodeResult[A]]): X = { 74 | if (d.isError) { 75 | e(d.message.get, d.history.get) 76 | } else { 77 | f(d.value.get) match { 78 | case Left(x) => x 79 | case Right(a) => loop(a, e, f) 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/EncodeJson.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Json.* 4 | import JsonIdentity.* 5 | import EncodeJsonNumber.* 6 | 7 | /** 8 | * Encode an arbitrary value as a JSON value. 9 | * 10 | * @author Tony Morris 11 | */ 12 | trait EncodeJson[A] { 13 | 14 | /** 15 | * Encode the given value. Alias for `encode`. 16 | */ 17 | def apply(a: A): Json = encode(a) 18 | 19 | /** 20 | * Encode the given value. 21 | */ 22 | def encode(a: A): Json 23 | 24 | /** 25 | * Contravariant functor. 26 | */ 27 | def contramap[B](f: B => A): EncodeJson[B] = { 28 | EncodeJson(b => apply(f(b))) 29 | } 30 | 31 | /** 32 | * Transform the resulting Json instance. 33 | */ 34 | def mapJson(f: Json => Json): EncodeJson[A] = { 35 | val original = this 36 | (a: A) => f(original(a)) 37 | } 38 | 39 | /** 40 | * Split on this encoder and the given encoder. 41 | */ 42 | def <&>[B](x: => EncodeJson[B]): EncodeJson[Either[A, B]] = { 43 | EncodeJson { 44 | case Left(a) => apply(a) 45 | case Right(b) => x(b) 46 | } 47 | } 48 | } 49 | 50 | object EncodeJson extends EncodeJsons with EncodeJsonMacro { 51 | def apply[A](f: A => Json): EncodeJson[A] = 52 | (a: A) => f(a) 53 | 54 | def of[A: EncodeJson]: EncodeJson[A] = implicitly[EncodeJson[A]] 55 | } 56 | 57 | trait EncodeJsons extends GeneratedEncodeJsons { 58 | /* TODO: Come back to this. 59 | def contrazip[A, B](e: EncodeJson[A \/ B]): (EncodeJson[A], EncodeJson[B]) = 60 | (EncodeJson(a => e(a.left)), EncodeJson(b => e(b.right))) 61 | */ 62 | 63 | implicit val JsonEncodeJson: EncodeJson[Json] = 64 | EncodeJson(q => q) 65 | 66 | implicit val HCursorEncodeJson: EncodeJson[HCursor] = 67 | EncodeJson(q => q.focus) 68 | 69 | implicit val UnitEncodeJson: EncodeJson[Unit] = 70 | EncodeJson(_ => jEmptyObject) 71 | 72 | implicit def ListEncodeJson[A](implicit e: EncodeJson[A]): EncodeJson[List[A]] = 73 | EncodeJson(a => jArray(a.map(e(_)))) 74 | 75 | implicit def VectorEncodeJson[A](implicit e: EncodeJson[List[A]]): EncodeJson[Vector[A]] = 76 | EncodeJson(a => e(a.toList)) 77 | 78 | implicit def StreamEncodeJson[A](implicit e: EncodeJson[A]): EncodeJson[Stream[A]] = 79 | EncodeJson(a => jArray(a.toList.map(e(_)))) 80 | 81 | implicit val StringEncodeJson: EncodeJson[String] = 82 | EncodeJson(jString) 83 | 84 | implicit val UUIDEncodeJson: EncodeJson[java.util.UUID] = 85 | StringEncodeJson.contramap(_.toString) 86 | 87 | implicit val DoubleEncodeJson: EncodeJson[Double] = 88 | EncodeJson(a => a.asPossibleJsonNumber.fold(jNull)(_.asJson)) 89 | 90 | implicit val FloatEncodeJson: EncodeJson[Float] = 91 | EncodeJson(a => a.asPossibleJsonNumber.fold(jNull)(_.asJson)) 92 | 93 | implicit val IntEncodeJson: EncodeJson[Int] = 94 | EncodeJson(a => a.asJsonNumber.asJson) 95 | 96 | implicit val LongEncodeJson: EncodeJson[Long] = 97 | EncodeJson(a => a.asJsonNumber.asJson) 98 | 99 | implicit val ShortEncodeJson: EncodeJson[Short] = 100 | EncodeJson(a => a.asJsonNumber.asJson) 101 | 102 | implicit val ByteEncodeJson: EncodeJson[Byte] = 103 | EncodeJson(a => a.asJsonNumber.asJson) 104 | 105 | implicit val BigDecimalEncodeJson: EncodeJson[BigDecimal] = 106 | EncodeJson(a => a.asJsonNumber.asJson) 107 | 108 | implicit val BigIntEncodeJson: EncodeJson[BigInt] = 109 | EncodeJson(a => a.asJsonNumber.asJson) 110 | 111 | implicit val BooleanEncodeJson: EncodeJson[Boolean] = 112 | EncodeJson(jBool) 113 | 114 | implicit val CharEncodeJson: EncodeJson[Char] = 115 | EncodeJson(a => jString(a.toString)) 116 | 117 | implicit val JDoubleEncodeJson: EncodeJson[java.lang.Double] = 118 | EncodeJson(a => a.asPossibleJsonNumber.fold(jNull)(_.asJson)) 119 | 120 | implicit val JFloatEncodeJson: EncodeJson[java.lang.Float] = 121 | EncodeJson(a => a.asPossibleJsonNumber.fold(jNull)(_.asJson)) 122 | 123 | implicit val JIntegerEncodeJson: EncodeJson[java.lang.Integer] = 124 | EncodeJson(a => a.asJsonNumber.asJson) 125 | 126 | implicit val JLongEncodeJson: EncodeJson[java.lang.Long] = 127 | EncodeJson(a => a.asJsonNumber.asJson) 128 | 129 | implicit val JShortEncodeJson: EncodeJson[java.lang.Short] = 130 | EncodeJson(a => a.asJsonNumber.asJson) 131 | 132 | implicit val JByteEncodeJson: EncodeJson[java.lang.Byte] = 133 | EncodeJson(a => a.asJsonNumber.asJson) 134 | 135 | implicit val JBooleanEncodeJson: EncodeJson[java.lang.Boolean] = 136 | EncodeJson(a => jBool(a.booleanValue)) 137 | 138 | implicit val JCharacterEncodeJson: EncodeJson[java.lang.Character] = 139 | EncodeJson(a => jString(a.toString)) 140 | 141 | implicit def OptionEncodeJson[A](implicit e: EncodeJson[A]): EncodeJson[Option[A]] = { 142 | EncodeJson(_ match { 143 | case None => jNull 144 | case Some(a) => e(a) 145 | }) 146 | } 147 | 148 | implicit def EitherEncodeJson[A, B](implicit ea: EncodeJson[A], eb: EncodeJson[B]): EncodeJson[Either[A, B]] = { 149 | EncodeJson(_ match { 150 | case Left(a) => jSingleObject("Left", ea(a)) 151 | case Right(b) => jSingleObject("Right", eb(b)) 152 | }) 153 | } 154 | 155 | implicit def MapEncodeJson[K, V](implicit K: EncodeJsonKey[K], e: EncodeJson[V]): EncodeJson[Map[K, V]] = { 156 | EncodeJson(x => 157 | jObjectAssocList( 158 | x.toList.map { case (k, v) => 159 | (K.toJsonKey(k), e(v)) 160 | } 161 | ) 162 | ) 163 | } 164 | 165 | implicit def SetEncodeJson[A](implicit e: EncodeJson[A]): EncodeJson[Set[A]] = { 166 | EncodeJson(ListEncodeJson[A].contramap((_: Set[A]).toList).apply(_)) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/EncodeJsonKey.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | /** A typeclass for encode an arbitrary value as a JSON key. 4 | * 5 | * @example {{{ 6 | * final case class Foo(value: String) 7 | * object Foo { 8 | * implicit val instance: EncodeJsonKey[Foo] = 9 | * EncodeJsonKey.from(_.value) 10 | * } 11 | * 12 | * EncodeJson.of[Map[Foo, Int]] 13 | * }}} 14 | */ 15 | trait EncodeJsonKey[A] { self => 16 | 17 | def toJsonKey(key: A): String 18 | 19 | final def contramap[B](f: B => A): EncodeJsonKey[B] = { (key: B) => 20 | self.toJsonKey(f(key)) 21 | } 22 | } 23 | 24 | object EncodeJsonKey { 25 | 26 | @inline def apply[A](implicit A: EncodeJsonKey[A]): EncodeJsonKey[A] = A 27 | 28 | def from[A](f: A => String): EncodeJsonKey[A] = { (key: A) => 29 | f(key) 30 | } 31 | 32 | implicit val StringEncodeJsonKey: EncodeJsonKey[String] = from(x => x) 33 | } 34 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/JsonIdentity.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | class JsonIdentity[J](val j: J) extends AnyVal { 4 | 5 | /** 6 | * Encode to a JSON value using the given implicit encoder. 7 | */ 8 | def jencode(implicit e: EncodeJson[J]): Json = e(j) 9 | 10 | /** 11 | * Encode to a JSON value using the given implicit encoder. Alias for `jencode`. 12 | */ 13 | def asJson(implicit e: EncodeJson[J]): Json = jencode 14 | 15 | /** 16 | * Encode to a JSONNumber. 17 | */ 18 | def asJsonNumber(implicit asn: EncodeJsonNumber[J]): JsonNumber = asn.encodeJsonNumber(j) 19 | 20 | /** 21 | * Encode to a JSONNumber, wrapped in a Some if it is valid, otherwise a None. 22 | */ 23 | def asPossibleJsonNumber(implicit asn: EncodePossibleJsonNumber[J]): Option[JsonNumber] = 24 | asn.possiblyEncodeJsonNumber(j) 25 | } 26 | 27 | object JsonIdentity extends JsonIdentitys 28 | 29 | trait JsonIdentitys { 30 | implicit def ToJsonIdentity[J](k: J): JsonIdentity[J] = 31 | new JsonIdentity[J](k) 32 | 33 | implicit def FromJsonIdentity[J](k: JsonIdentity[J]): J = k.j 34 | } 35 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/JsonObject.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Json.* 4 | 5 | /** 6 | * A mapping from field to JSON value that maintains insertion order. 7 | * 8 | * @author Tony Morris 9 | */ 10 | sealed abstract class JsonObject { 11 | 12 | /** 13 | * Convert to a map. 14 | */ 15 | def toMap: Map[JsonField, Json] 16 | 17 | /** 18 | * Insert the given association. 19 | */ 20 | def +(f: JsonField, j: Json): JsonObject 21 | 22 | /** 23 | * Append the given association. 24 | */ 25 | def :+(fj: (JsonField, Json)): JsonObject 26 | 27 | /** 28 | * Prepend the given association. 29 | */ 30 | def +:(fj: (JsonField, Json)): JsonObject 31 | 32 | /** 33 | * Remove the given field association. 34 | */ 35 | def -(f: JsonField): JsonObject 36 | 37 | /** 38 | * Return the JSON value associated with the given field. 39 | */ 40 | def apply(f: JsonField): Option[Json] 41 | 42 | /** 43 | * Transform all associated JSON values. 44 | */ 45 | def withJsons(k: Json => Json): JsonObject 46 | 47 | /** 48 | * Returns true if there are no associations. 49 | */ 50 | def isEmpty: Boolean 51 | 52 | /** 53 | * Returns true if there is at least one association. 54 | */ 55 | def isNotEmpty: Boolean 56 | 57 | /** 58 | * Returns true if there is an association with the given field. 59 | */ 60 | def ??(f: JsonField): Boolean 61 | 62 | /** 63 | * Returns the list of associations in insertion order. 64 | */ 65 | def toList: List[JsonAssoc] 66 | 67 | /** 68 | * Returns all associated values in insertion order. 69 | */ 70 | def values: List[Json] 71 | 72 | /** 73 | * Returns all association keys in insertion order. 74 | */ 75 | def fields: List[JsonField] 76 | 77 | /** 78 | * Returns all association keys in arbitrary order. 79 | */ 80 | def fieldSet: Set[JsonField] 81 | 82 | /** 83 | * Map Json values. 84 | */ 85 | def map(f: Json => Json): JsonObject 86 | 87 | /** 88 | * Returns the number of associations. 89 | */ 90 | def size: Int 91 | } 92 | 93 | private[argonaut] case class JsonObjectInstance( 94 | fieldsMap: Map[JsonField, Json] = Map.empty, 95 | orderedFields: Vector[JsonField] = Vector.empty 96 | ) extends JsonObject { 97 | 98 | def toMap: Map[JsonField, Json] = fieldsMap 99 | 100 | def +(f: JsonField, j: Json): JsonObject = { 101 | if (fieldsMap.contains(f)) { 102 | copy(fieldsMap = fieldsMap.updated(f, j)) 103 | } else { 104 | copy(fieldsMap = fieldsMap.updated(f, j), orderedFields = orderedFields :+ f) 105 | } 106 | } 107 | 108 | def :+(fj: (JsonField, Json)): JsonObject = { 109 | this.+(fj._1, fj._2) 110 | } 111 | 112 | def +:(fj: (JsonField, Json)): JsonObject = { 113 | val (f, j) = fj 114 | if (fieldsMap.contains(f)) 115 | copy(fieldsMap = fieldsMap.updated(f, j)) 116 | else 117 | copy(fieldsMap = fieldsMap.updated(f, j), orderedFields = f +: orderedFields) 118 | } 119 | 120 | def -(f: JsonField): JsonObject = 121 | copy(fieldsMap = fieldsMap - f, orderedFields = orderedFields.filterNot(_ == f)) 122 | 123 | def apply(f: JsonField): Option[Json] = fieldsMap.get(f) 124 | 125 | def withJsons(k: Json => Json): JsonObject = map(k) 126 | 127 | def isEmpty: Boolean = fieldsMap.isEmpty 128 | 129 | def isNotEmpty: Boolean = !isEmpty 130 | 131 | def ??(f: JsonField): Boolean = fieldsMap.contains(f) 132 | 133 | def toList: List[JsonAssoc] = orderedFields.map(field => (field, fieldsMap(field))).toList 134 | 135 | def values: List[Json] = orderedFields.map(field => fieldsMap(field)).toList 136 | 137 | def fields: List[JsonField] = orderedFields.toList 138 | 139 | def fieldSet: Set[JsonField] = orderedFields.toSet 140 | 141 | def map(f: Json => Json): JsonObject = copy(fieldsMap = fieldsMap.foldLeft(Map.empty[JsonField, Json]) { 142 | case (acc, (key, value)) => acc.updated(key, f(value)) 143 | }) 144 | 145 | def size: Int = fields.size 146 | 147 | override def toString: String = { 148 | s"object[${fieldsMap.map(_.toString).mkString(",")}]" 149 | } 150 | 151 | override def equals(o: Any) = { 152 | o match { 153 | case JsonObjectInstance(otherMap, _) => fieldsMap == otherMap 154 | case _ => false 155 | } 156 | } 157 | 158 | override def hashCode = fieldsMap.hashCode 159 | } 160 | 161 | object JsonObject extends JsonObjects { 162 | 163 | /** 164 | * Construct an empty association. 165 | */ 166 | def empty: JsonObject = JsonObjectInstance() 167 | 168 | /** 169 | * Construct with a single association. 170 | */ 171 | def single(f: JsonField, j: Json): JsonObject = { 172 | JsonObject.empty + (f, j) 173 | } 174 | 175 | /** 176 | * Construct an object from a Iterable instance. 177 | */ 178 | def fromIterable(t: Iterable[(JsonField, Json)]): JsonObject = { 179 | t.foldLeft(empty)(_ :+ _) 180 | } 181 | } 182 | 183 | trait JsonObjects {} 184 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/Parse.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | /** 4 | * Library functions for parsing json. 5 | */ 6 | trait Parse[A] { 7 | 8 | /** 9 | * Parses the value and either returns a list of the failures from parsing the string 10 | * or an instance of the Json type if parsing succeeds. 11 | */ 12 | def parse(value: A): Either[String, Json] 13 | 14 | /** 15 | * Parses the value and executes one of the given functions, depending on the parse outcome. 16 | * 17 | * @param success Run this function if the parse succeeds. 18 | * @param failure Run this function if the parse produces a failure. 19 | */ 20 | def parseWith[X](value: A, success: Json => X, failure: String => X): X = { 21 | parse(value).fold(failure, success) 22 | } 23 | 24 | /** 25 | * Parses the value and executes one of the given functions, depending on the parse outcome. 26 | * Any error message is ignored. 27 | * 28 | * @param success Run this function if the parse succeeds. 29 | * @param failure Run this function if the parse produces a failure. 30 | */ 31 | def parseOr[X](value: A, success: Json => X, failure: => X): X = { 32 | parseWith(value, success, _ => failure) 33 | } 34 | 35 | /** 36 | * Parses the value to a possible JSON value. 37 | */ 38 | def parseOption(value: A): Option[Json] = { 39 | parse(value).toOption 40 | } 41 | 42 | /** 43 | * Parses the value and decodes it returning a list of all the failures stemming from 44 | * either the JSON parsing or the decoding. 45 | */ 46 | def decode[X: DecodeJson](value: A): Either[Either[String, (String, CursorHistory)], X] = for { 47 | json <- parse(value).left.map(Left.apply) 48 | decoded <- json.jdecode[X].fold((msg, history) => Left(Right((msg, history))), Right.apply) 49 | } yield decoded 50 | 51 | /** 52 | * Parses the value into a JSON value and if it succeeds, decodes to a data-type. 53 | * 54 | * @param success Run this function if the parse produces a success. 55 | * @param parsefailure Run this function if the parse produces a failure. 56 | * @param decodefailure Run this function if the decode produces a failure. 57 | */ 58 | def decodeWith[B, X: DecodeJson]( 59 | value: A, 60 | success: X => B, 61 | parsefailure: String => B, 62 | decodefailure: (String, CursorHistory) => B 63 | ): B = { 64 | val handleFailure: Either[String, (String, CursorHistory)] => B = 65 | _.fold[B](parsefailure, { case (m, h) => decodefailure(m, h) }) 66 | decodeWithEither[B, X](value, success, handleFailure) 67 | } 68 | 69 | /** 70 | * Parses the value into a JSON value and if it succeeds, decodes to a data-type. 71 | * 72 | * @param success Run this function if the parse produces a success. 73 | * @param failure Run this function if the parse produces a failure. 74 | */ 75 | def decodeWithEither[B, X: DecodeJson]( 76 | value: A, 77 | success: X => B, 78 | failure: Either[String, (String, CursorHistory)] => B 79 | ): B = { 80 | decode(value).fold(failure, success) 81 | } 82 | 83 | /** 84 | * Parses the value into a JSON value and if it succeeds, decodes to a data-type. 85 | * 86 | * @param success Run this function if the parse produces a success. 87 | * @param failure Run this function if the parse produces a failure. 88 | */ 89 | def decodeWithMessage[B, X: DecodeJson](value: A, success: X => B, failure: String => B): B = { 90 | decodeWith(value, success, failure, (m, h) => failure(m + ": " + h.toString)) 91 | } 92 | 93 | /** 94 | * Parses the value into a JSON value and if it succeeds, decodes to a data-type. 95 | * 96 | * @param success Run this function if the parse produces a success. 97 | * @param default Return this value of the parse or decode fails. 98 | */ 99 | def decodeOr[B, X: DecodeJson](value: A, success: X => B, default: => B): B = { 100 | decodeWith[B, X](value, success, _ => default, (_, _) => default) 101 | } 102 | 103 | /** 104 | * Parses and decodes the value to a possible JSON value. 105 | */ 106 | def decodeOption[X: DecodeJson](value: A): Option[X] = { 107 | decode(value).toOption 108 | } 109 | 110 | /** 111 | * Parses and decodes the value to a possible JSON value. 112 | */ 113 | def decodeEither[X: DecodeJson](value: A): Either[String, X] = { 114 | decodeWithMessage[Either[String, X], X](value, Right.apply, Left.apply) 115 | } 116 | } 117 | 118 | /** 119 | * Library functions for parsing json. 120 | */ 121 | object Parse extends Parse[String] { 122 | 123 | /** 124 | * Parses the string value and either returns a list of the failures from parsing the string 125 | * or an instance of the Json type if parsing succeeds. 126 | */ 127 | def parse(value: String): Either[String, Json] = { 128 | JsonParser.parse(value) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/ParseWrap.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | /** 4 | * Utility for building the argonaut API over 5 | * various types. This is used to implement 6 | * StringWrap, and it is expected that it would 7 | * be used by integrations with other toolkits 8 | * to provide an argonaut API on their types. 9 | */ 10 | class ParseWrap[A](value: A, parser: Parse[A]) { 11 | 12 | /** 13 | * Parses the string value and either returns a list of the failures from parsing the string 14 | * or an instance of the Json type if parsing succeeds. 15 | */ 16 | def parse: Either[String, Json] = { 17 | parser.parse(value) 18 | } 19 | 20 | /** 21 | * Parses the string value and executes one of the given functions, depending on the parse outcome. 22 | * 23 | * @param success Run this function if the parse succeeds. 24 | * @param failure Run this function if the parse produces a failure. 25 | */ 26 | def parseWith[X](success: Json => X, failure: String => X): X = { 27 | parser.parseWith(value, success, failure) 28 | } 29 | 30 | /** 31 | * Parses the string value and executes one of the given functions, depending on the parse outcome. 32 | * Any error message is ignored. 33 | * 34 | * @param success Run this function if the parse succeeds. 35 | * @param failure Run this function if the parse produces a failure. 36 | */ 37 | def parseOr[X](success: Json => X, failure: => X): X = 38 | parser.parseOr(value, success, failure) 39 | 40 | /** 41 | * Parses the string value to a possible JSON value. 42 | */ 43 | def parseOption: Option[Json] = 44 | parser.parseOption(value) 45 | 46 | /** 47 | * Parses the string value and decodes it returning a list of all the failures stemming from 48 | * either the JSON parsing or the decoding. 49 | */ 50 | def decode[X: DecodeJson]: Either[Either[String, (String, CursorHistory)], X] = 51 | parser.decode(value) 52 | 53 | /** 54 | * Parses the string value into a JSON value and if it succeeds, decodes to a data-type. 55 | * 56 | * @param success Run this function if the parse produces a success. 57 | * @param parsefailure Run this function if the parse produces a failure. 58 | * @param decodefailure Run this function if the decode produces a failure. 59 | */ 60 | def decodeWith[Y, X: DecodeJson]( 61 | success: X => Y, 62 | parsefailure: String => Y, 63 | decodefailure: (String, CursorHistory) => Y 64 | ): Y = 65 | parser.decodeWith(value, success, parsefailure, decodefailure) 66 | 67 | /** 68 | * Parses the string value into a JSON value and if it succeeds, decodes to a data-type. 69 | * 70 | * @param success Run this function if the parse produces a success. 71 | * @param failure Run this function if the parse produces a failure. 72 | */ 73 | def decodeWithEither[Y, X: DecodeJson](success: X => Y, failure: Either[String, (String, CursorHistory)] => Y): Y = 74 | parser.decodeWithEither(value, success, failure) 75 | 76 | /** 77 | * Parses the string value into a JSON value and if it succeeds, decodes to a data-type. 78 | * 79 | * @param success Run this function if the parse produces a success. 80 | * @param failure Run this function if the parse produces a failure. 81 | */ 82 | def decodeWithMessage[Y, X: DecodeJson](success: X => Y, failure: String => Y): Y = 83 | parser.decodeWithMessage(value, success, failure) 84 | 85 | /** 86 | * Parses the string value into a JSON value and if it succeeds, decodes to a data-type. 87 | * 88 | * @param success Run this function if the parse produces a success. 89 | * @param default Return this value of the parse or decode fails. 90 | */ 91 | def decodeOr[Y, X: DecodeJson](success: X => Y, default: => Y): Y = 92 | parser.decodeOr(value, success, default) 93 | 94 | /** 95 | * Parses and decodes the string value to a possible JSON value. 96 | */ 97 | def decodeOption[X: DecodeJson]: Option[X] = 98 | parser.decodeOption(value) 99 | 100 | /** 101 | * Parses and decodes the string value to a possible JSON value. 102 | */ 103 | def decodeEither[X: DecodeJson]: Either[String, X] = 104 | parser.decodeEither(value) 105 | } 106 | -------------------------------------------------------------------------------- /argonaut/shared/src/main/scala/argonaut/StringWrap.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | /** 4 | * Wraps a `String` value and provides methods, particularly for parsing. 5 | * 6 | * @author Tony Morris 7 | */ 8 | sealed abstract class StringWrap { 9 | 10 | /** 11 | * Underlying string value. 12 | */ 13 | val value: String 14 | 15 | /* 16 | * Construct a pair of the key and JSON value. 17 | * 18 | * Example: 19 | * {{{ 20 | * ("key1" := "value1") ->: ("key2" := "value2") ->: jEmptyObject 21 | * }}} 22 | */ 23 | def :=[A: EncodeJson](a: A) = 24 | (value, implicitly[EncodeJson[A]].apply(a)) 25 | 26 | /* 27 | * Construct an optional pair of the key and JSON value. 28 | * 29 | * This is an alias for `:?=` which is now preferred due to 30 | * better precendence. 31 | * 32 | * Example: 33 | * {{{ 34 | * ("key1" :=? None) ->?: ("key2" :=? Some("value2")) ->?: jEmptyObject 35 | * }}} 36 | */ 37 | def :=?[A: EncodeJson](a: Option[A]) = 38 | this :?= a 39 | 40 | /* 41 | * Construct an optional pair of the key and JSON value. 42 | * 43 | * Example: 44 | * {{{ 45 | * ("key1" :?= None) ->?: ("key2" :?= Some("value2")) ->?: jEmptyObject 46 | * }}} 47 | */ 48 | def :?=[A: EncodeJson](a: Option[A]) = 49 | a map (aa => (value, implicitly[EncodeJson[A]].apply(aa))) 50 | } 51 | 52 | object StringWrap extends StringWraps 53 | 54 | /** 55 | * Constructors and other utilities for wrapped string values. 56 | * 57 | * @author Tony Morris 58 | */ 59 | trait StringWraps { 60 | 61 | /** 62 | * Implicitly wraps the given string value. 63 | */ 64 | implicit def StringToStringWrap(s: String): StringWrap = 65 | new StringWrap { 66 | val value = s 67 | } 68 | 69 | /** 70 | * Implicitly wraps the given string value with parse API. 71 | */ 72 | implicit def StringToParseWrap(s: String): ParseWrap[String] = 73 | new ParseWrap(s, Parse) 74 | } 75 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala-2/argonaut/TestCompat.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import shapeless.ops.product.ToTuple 4 | 5 | object TestCompat { 6 | implicit class AsTupleOps[A](private val value: A) extends AnyVal { 7 | def asTuple[B](implicit toTuple: ToTuple.Aux[A, B]): Option[B] = 8 | Some(toTuple.apply(value)) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala-3/argonaut/CodecSpecificationScala3.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.scalacheck.Arbitrary 4 | import org.specs2.scalacheck.ScalaCheckFunction1 5 | 6 | object CodecSpecificationScala3 extends ArgonautSpec { 7 | def encodedecode[A: EncodeJson: DecodeJson: Arbitrary]: ScalaCheckFunction1[A, Boolean] = { 8 | val aCodec = CodecJson.derived[A] 9 | prop[A, Boolean] { a => 10 | CodecJson.codecLaw(aCodec)(a) 11 | } 12 | } 13 | 14 | def is = s2""" 15 | Codec 16 | CodecJson[MyList[A]] derived ${testMyList} 17 | """ 18 | 19 | private def testMyList = { 20 | encodedecode[MyList[String]] 21 | encodedecode[MyList[Int]] 22 | } 23 | } 24 | 25 | case class MyList[A](head: A, tail: Option[MyList[A]]) 26 | 27 | object MyList { 28 | implicit def decodeJson[A: DecodeJson]: DecodeJson[MyList[A]] = 29 | DecodeJson.derive[MyList[A]] 30 | 31 | implicit def encodeJson[A: EncodeJson]: EncodeJson[MyList[A]] = 32 | EncodeJson.derive[MyList[A]] 33 | 34 | @annotation.tailrec 35 | private def fromList[A](list: List[A], acc: MyList[A]): MyList[A] = { 36 | list match { 37 | case x :: xs => 38 | fromList(xs, MyList(x, Some(acc))) 39 | case Nil => 40 | acc 41 | } 42 | } 43 | 44 | implicit def arbitrary[A: Arbitrary]: Arbitrary[MyList[A]] = Arbitrary( 45 | implicitly[Arbitrary[(A, List[A])]].arbitrary.map { case (x, xs) => 46 | fromList(xs, MyList(x, None)) 47 | } 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala-3/argonaut/TestCompat.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scala.deriving.Mirror.ProductOf 4 | 5 | object TestCompat { 6 | extension [A <: scala.Product](value: A) { 7 | def asTuple(using mirror: ProductOf[A]): Option[mirror.MirroredElemTypes] = 8 | Some(Tuple.fromProductTyped(value)) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/ACursorSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.scalacheck.* 4 | import Prop.* 5 | import Arbitrary.* 6 | import Gen.* 7 | import Data.* 8 | import Argonaut.* 9 | 10 | object ACursorSpecification extends ArgonautSpec { 11 | def is = s2""" 12 | ACursor 13 | History must reflect success after single step. $singleStepSuccess 14 | History must reflect success after multiple steps. $multiStepSuccess 15 | Nothing accept reattempt may occur after failure. $reattemptAfterFailure 16 | """ 17 | 18 | def singleStepSuccess = prop((j: Json) => { 19 | forAll((op: TestOp) => { 20 | val r = step(j.acursor, op) 21 | if (r.succeeded) 22 | r.history.head.forall(h => h.isReattempt || h.succeeded) 23 | else 24 | r.history.head.exists(_.failed) 25 | }) 26 | }) 27 | def multiStepSuccess = prop((j: Json) => { 28 | forAll((op: List[TestOp]) => { 29 | val r = op.foldLeft(j.acursor)((acc, op) => step(acc, op)) 30 | if (r.succeeded) 31 | r.history.head.forall(h => h.isReattempt || h.succeeded) 32 | else 33 | r.history.head.exists(_.failed) 34 | }) 35 | }) 36 | 37 | def reattemptAfterFailure = prop((j: Json) => { 38 | forAll((op: List[TestOp]) => { 39 | val r = op.foldLeft(j.acursor)((acc, op) => step(acc, op)) 40 | r.history.toList.inits.toList.forall { 41 | case init :+ penultimate :+ last => 42 | last.succeeded || last.isReattempt || penultimate.isReattempt 43 | case init :+ last => 44 | last.succeeded || last.isReattempt || init.isEmpty 45 | case _ => 46 | true 47 | } 48 | }) 49 | }) 50 | 51 | def step(start: ACursor, op: TestOp): ACursor = 52 | op match { 53 | case First => 54 | start.first 55 | case Last => 56 | start.last 57 | case Down => 58 | if (start.focus.exists(_.isObject)) 59 | withField(start, _.downField(_)) 60 | else if (start.focus.exists(_.isArray)) 61 | start.downArray 62 | else 63 | start 64 | case Up => 65 | start.up 66 | case Left => 67 | start.left 68 | case Right => 69 | start.right 70 | case Sibling => 71 | withField(start, _.field(_)) 72 | case Delete => 73 | start.delete 74 | case Set(j: Json) => 75 | start.set(j) 76 | case Reattempt => 77 | start.reattempt 78 | } 79 | 80 | def field(j: Json): Option[JsonField] = 81 | j.obj.flatMap(_.fields.headOption) 82 | 83 | def withField(a: ACursor, f: (ACursor, JsonField) => ACursor): ACursor = 84 | a.focus.flatMap(c => field(c).map(field => f(a, field))).getOrElse(a) 85 | 86 | trait TestOp 87 | case object Reattempt extends TestOp 88 | case object First extends TestOp 89 | case object Sibling extends TestOp 90 | case object Last extends TestOp 91 | case object Down extends TestOp 92 | case object Up extends TestOp 93 | case object Left extends TestOp 94 | case object Right extends TestOp 95 | case object Delete extends TestOp 96 | case class Set(j: Json) extends TestOp 97 | 98 | implicit val ArbitraryTestOp: Arbitrary[TestOp] = 99 | Arbitrary( 100 | Gen.frequency( 101 | (9, Gen.oneOf(Down, Up, Left, Right, Delete)), 102 | (1, arbitrary[Json].map(Set.apply)) 103 | ) 104 | ) 105 | 106 | } 107 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/CodecNumberSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Argonaut.* 4 | 5 | object CodecNumberSpecification extends ArgonautSpec { 6 | 7 | def is = s2""" 8 | Codec Numbers 9 | double that is not NaN or infinity encodes to number $double 10 | int always encodes to number $intToNumber 11 | long always encodes to number $longToNumber 12 | """ 13 | 14 | def double = prop { (xs: List[Double]) => 15 | xs.filter(x => !x.isNaN && !x.isInfinity).asJson.array.forall(_.forall(_.isNumber)) 16 | } 17 | 18 | def intToNumber = prop { (xs: List[Int]) => xs.asJson.array.forall(_.forall(_.isNumber)) } 19 | 20 | def longToNumber = prop { (xs: List[Long]) => xs.asJson.array.forall(_.forall(_.isNumber)) } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/CodecOptionSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Argonaut.* 4 | 5 | object CodecOptionSpecification extends ArgonautSpec { 6 | case class Thing(value: Option[String]) 7 | 8 | implicit def ThingCodecJson: CodecJson[Thing] = 9 | casecodec1(Thing.apply, (a: Thing) => Option(a.value))("value") 10 | 11 | def is = s2""" 12 | Codec Option 13 | handles missing field ${jEmptyObject.as[Thing] must_== DecodeResult.ok(Thing(None))} 14 | handles null field ${Json.obj("value" := jNull).as[Thing] must_== DecodeResult.ok(Thing(None))} 15 | handles set field $setField 16 | handles missing nested fields using as[T] $missingNestedAs 17 | handles missing nested fields using get[T] $missingNestedGet 18 | """ 19 | 20 | def setField = prop { (value: String) => 21 | Json.obj("value" := value).as[Thing] must_== DecodeResult.ok(Thing(Some(value))) 22 | } 23 | 24 | def missingNestedAs = prop { (value: String) => 25 | val third = Json 26 | .obj("first" := jEmptyObject) 27 | .hcursor 28 | .downField("first") 29 | .downField("second") 30 | .downField("third") 31 | .as[Option[String]] 32 | third must_== DecodeResult.ok(None) 33 | } 34 | 35 | def missingNestedGet = prop { (value: String) => 36 | val third = 37 | Json.obj("first" := jEmptyObject).hcursor.downField("first").downField("second").get[Option[String]]("third") 38 | third must_== DecodeResult.ok(None) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/CursorSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.scalacheck.Prop.* 4 | import Data.* 5 | import Argonaut.* 6 | import scalaz.* 7 | 8 | object CursorSpecification extends ArgonautSpec { 9 | def is = s2""" 10 | Cursor 11 | Json->Cursor->Json ${prop((j: Json) => (-(+j)) === j)} 12 | Json->Cursor->withFocus ${prop((j: Json) => (+j).focus === j)} 13 | withFocus on focus changes nothing ${prop((c: Cursor) => c.withFocus(_ => c.focus) === c)} 14 | withFocus identity changes nothing ${prop((c: Cursor) => c.withFocus(j => j) === c)} 15 | >-> aliases withFocus ${prop((c: Cursor, f: Json => Json) => (c withFocus f) === (c >-> f))} 16 | set gives focus ${prop((c: Cursor, j: Json) => (c set j).focus === j)} 17 | := aliases set ${prop((c: Cursor, j: Json) => (c set j) === (c := j))} 18 | lefts head is left ${prop((c: Cursor) => 19 | c.lefts forall (a => a.headOption == c.left.map(_.focus)) 20 | )} 21 | rights head is right ${prop((c: Cursor) => 22 | c.rights forall (a => a.headOption == c.right.map(_.focus)) 23 | )} 24 | first has no lefts ${prop((c: Cursor) => c.first forall (_.left.isEmpty))} 25 | last has no rights ${prop((c: Cursor) => c.last forall (_.right.isEmpty))} 26 | left->right ${prop((c: Cursor) => c.left forall (_.right exists (_ == c)))} 27 | right->left ${prop((c: Cursor) => c.right forall (_.left exists (_ == c)))} 28 | downArray->up ${prop((c: Cursor) => c.downArray forall (_.up exists (_ == c)))} 29 | downArray $downArray 30 | downAt constant $downAtConstant 31 | downAt constant true is same as down array $downAtConstantDownArray 32 | downAt $downAt 33 | first $first 34 | last $last 35 | leftAt $leftAt 36 | left $left 37 | rightAt $rightAt 38 | right $right 39 | right is same as rightN(1) $rightOne 40 | rightN(0) is a no op $rightNoOp 41 | leftN $leftN 42 | rightN $rightN 43 | find $find 44 | """ 45 | 46 | def downArray = prop((x: Json, xs: List[Json]) => jArray(x :: xs).cursor.downArray.map(_.focus) must_== Some(x)) 47 | 48 | def downAtConstant = 49 | prop((x: Json, xs: List[Json]) => jArray(x :: xs).cursor.downAt(_ => true).map(_.focus) must_== Some(x)) 50 | 51 | def downAtConstantDownArray = prop((xs: List[Json]) => 52 | jArray(xs).cursor.downAt(_ => true).map(_.focus) must_== jArray(xs).cursor.downArray.map(_.focus) 53 | ) 54 | 55 | def downAt = prop((ys: List[Json], x: Json, xs: List[Json]) => 56 | jArray(ys ::: (x :: xs)).cursor.downAt(_ == x).map(_.focus) must_== Some(x) 57 | ) 58 | 59 | def first = prop((y: Json, ys: List[Json], x: Json, xs: List[Json]) => 60 | jArray((y :: ys) ::: (x :: xs)).cursor.downAt(_ == x).flatMap(_.first).map(_.focus) must_== Some(y) 61 | ) 62 | 63 | def last = prop((ys: List[Json], x: Json, xs: List[Json], z: Json) => 64 | jArray(ys ::: (x :: xs) ::: List(z)).cursor.downAt(_ == x).flatMap(_.last).map(_.focus) must_== Some(z) 65 | ) 66 | 67 | def leftAt = prop((xx: Json, x: Json, xs: List[Json]) => 68 | jArray(xx :: x :: xs).cursor.downN(1).flatMap(_.leftAt(_ => true)).map(_.focus) must_== Some(xx) 69 | ) 70 | 71 | def left = prop((xx: Json, x: Json, xs: List[Json]) => 72 | jArray(xx :: x :: xs).cursor.downN(1).flatMap(_.left).map(_.focus) must_== Some(xx) 73 | ) 74 | 75 | def rightAt = prop((xx: Json, x: Json, xs: List[Json]) => 76 | jArray(xx :: x :: xs).cursor.downArray.flatMap(_.rightAt(_ => true)).map(_.focus) must_== Some(x) 77 | ) 78 | 79 | def right = prop((x: Json, xs: List[Json]) => 80 | jArray(x :: xs).cursor.downArray.flatMap(_.right).map(_.focus) must_== xs.headOption 81 | ) 82 | 83 | def rightOne = prop((x: Json, xs: List[Json]) => 84 | jArray(x :: xs).cursor.downArray.flatMap(_.right).map(_.focus) must_== 85 | jArray(x :: xs).cursor.downArray.flatMap(_.rightN(1)).map(_.focus) 86 | ) 87 | 88 | def rightNoOp = prop((ys: List[Json], x: Json, xs: List[Json]) => 89 | jArray(x :: xs).cursor.downArray.map(_.focus) must_== 90 | jArray(x :: xs).cursor.downArray.flatMap(_.rightN(0)).map(_.focus) 91 | ) 92 | 93 | def leftN = prop((ys: List[Json], x: Json, xs: List[Json]) => 94 | !ys.contains(x) ==> { 95 | jArray(ys ::: (x :: xs)).cursor 96 | .downAt(_ == x) 97 | .flatMap(_.leftN(Math.max(ys.size, 1))) 98 | .map(_.focus) must_== ys.headOption 99 | } 100 | ) 101 | 102 | def rightN = prop((ys: List[Json], x: Json, xs: List[Json]) => 103 | !ys.contains(x) ==> { 104 | jArray(ys ::: (x :: xs)).cursor 105 | .downAt(_ == x) 106 | .flatMap(_.rightN(Math.max(xs.size, 1))) 107 | .map(_.focus) must_== xs.lastOption 108 | } 109 | ) 110 | 111 | def find = prop((x: Json, xs: List[Json]) => 112 | jArray(x :: xs).cursor.downArray.flatMap(_.find(_ => true)).map(_.focus) === Some(x) 113 | ) 114 | } 115 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/DecodeJsonSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Argonaut.* 4 | 5 | object DecodeJsonSpecification extends ArgonautSpec { 6 | def is = s2""" 7 | DecodeJson flatMapCursor 8 | Successful flatMap ${successfulFlatMapCursor} 9 | Failing flatMap ${failingFlatMapCursor} 10 | DecodeJson Witness Compilation 11 | Witness basics ${ok} 12 | Witness tuples ${ok} 13 | Witness auto ${ok} 14 | Witness derived ${ok} 15 | DecodeJson derive 16 | BackTicks ${derived.testBackTicksDecodeJson} 17 | """ 18 | 19 | def successfulFlatMapCursor = { 20 | prop { (key: String, n: Int) => 21 | val json = (key, n.jencode) ->: jEmptyObject 22 | val decodeJson = DecodeJson.of[Int].flatMapCursor(_.get[HCursor](key)) 23 | decodeJson.decodeJson(json) must beEqualTo(DecodeResult.ok(n)) 24 | } 25 | } 26 | 27 | def failingFlatMapCursor = { 28 | prop { (key: String, n: Int) => 29 | val json = (key, n.jencode) ->: jEmptyObject 30 | val decodeJson = DecodeJson.of[Int].flatMapCursor(_.get[HCursor]("TOTALLYNOTLIKELYTOBERANDOMLYGENERATEDTEXT")) 31 | val failure = DecodeResult.fail( 32 | "Attempt to decode value on failed cursor.", 33 | CursorOp.failedOp(CursorOpDownField("TOTALLYNOTLIKELYTOBERANDOMLYGENERATEDTEXT")) +: CursorHistory.empty 34 | ) 35 | decodeJson.decodeJson(json) must beEqualTo(failure) 36 | } 37 | } 38 | 39 | object primitives { 40 | DecodeJson.of[String] 41 | DecodeJson.of[Int] 42 | DecodeJson.of[Boolean] 43 | DecodeJson.of[Long] 44 | DecodeJson.of[Double] 45 | DecodeJson.of[Short] 46 | DecodeJson.of[Byte] 47 | DecodeJson.of[Option[Int]] 48 | DecodeJson.of[Option[String]] 49 | } 50 | 51 | object tuples { 52 | DecodeJson.of[(String, Int)] 53 | DecodeJson.of[(String, Int, Boolean)] 54 | DecodeJson.of[(String, Int, Boolean, Long)] 55 | DecodeJson.of[(String, Int, Boolean, Long, Double)] 56 | } 57 | 58 | object derived { 59 | 60 | implicit def ShapeDecodeJson: DecodeJson[Shape] = DecodeJson.derive[Circle] ||| DecodeJson.derive[Square] 61 | DecodeJson.of[Shape] 62 | 63 | implicit def ProductDecodeJson: DecodeJson[Product] = DecodeJson.derive[Product] 64 | implicit def OrderLineDecodeJson: DecodeJson[OrderLine] = DecodeJson.derive[OrderLine] 65 | implicit def OrderDecodeJson: DecodeJson[Order] = DecodeJson.derive[Order] 66 | implicit def PersonDecodeJson: DecodeJson[Person] = DecodeJson.derive[Person] 67 | 68 | DecodeJson.of[Person] 69 | 70 | implicit def BackTicksDecodeJson: DecodeJson[BackTicks] = DecodeJson.derive[BackTicks] 71 | def testBackTicksDecodeJson = Parse.decodeEither[BackTicks]("""{"a.b.c": "test"}""") == Right(BackTicks("test")) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/EncodeJsonSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Argonaut.* 4 | 5 | object EncodeJsonSpecification extends ArgonautSpec { 6 | def is = s2""" 7 | EncodeJson mapJson 8 | Normal invocation ${mapJson} 9 | EncodeJson Witness Compilation 10 | Witness basics ${ok} 11 | Witness tuples ${ok} 12 | Witness auto ${ok} 13 | Witness derived ${ok} 14 | EncodeJson derive 15 | BackTicks ${derived.testBackTicksEncodeJson} 16 | """ 17 | 18 | def mapJson = { 19 | prop { (key: String, n: Int) => 20 | val json = (key, n.jencode) ->: jEmptyObject 21 | val encodeJson = EncodeJson.of[Int].mapJson(j => (key, j) ->: jEmptyObject) 22 | encodeJson.encode(n) must beEqualTo(json) 23 | } 24 | } 25 | 26 | object primitives { 27 | EncodeJson.of[String] 28 | EncodeJson.of[Int] 29 | EncodeJson.of[Boolean] 30 | EncodeJson.of[Long] 31 | EncodeJson.of[Double] 32 | EncodeJson.of[Short] 33 | EncodeJson.of[Byte] 34 | EncodeJson.of[Option[Int]] 35 | EncodeJson.of[Option[String]] 36 | } 37 | 38 | object tuples { 39 | EncodeJson.of[(String, Int)] 40 | EncodeJson.of[(String, Int, Boolean)] 41 | EncodeJson.of[(String, Int, Boolean, Long)] 42 | EncodeJson.of[(String, Int, Boolean, Long, Double)] 43 | } 44 | 45 | object encodeJsonKey { 46 | final case class Foo(value: String) 47 | object Foo { 48 | implicit val instance: EncodeJsonKey[Foo] = EncodeJsonKey.from(_.value) 49 | } 50 | 51 | EncodeJson.of[Map[Foo, Int]] 52 | } 53 | 54 | object derived { 55 | implicit def ProductEncodeJson: EncodeJson[Product] = EncodeJson.derive[Product] 56 | implicit def OrderLineEncodeJson: EncodeJson[OrderLine] = EncodeJson.derive[OrderLine] 57 | implicit def OrderEncodeJson: EncodeJson[Order] = EncodeJson.derive[Order] 58 | implicit def PersonEncodeJson: EncodeJson[Person] = EncodeJson.derive[Person] 59 | 60 | EncodeJson.of[Person] 61 | 62 | implicit def BackTicksEncodeJson: EncodeJson[BackTicks] = EncodeJson.derive[BackTicks] 63 | def testBackTicksEncodeJson = BackTicks("test").jencode === ("a.b.c" := "test") ->: jEmptyObject 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/JsonFilesSpecBase.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.Argonaut.* 4 | import org.specs2.specification.core.Fragments 5 | import JsonFilesSpecBase.TestData 6 | 7 | object JsonFilesSpecBase { 8 | final case class TestData(fileName: String, jsonString: String) 9 | } 10 | 11 | abstract class JsonFilesSpecBase extends ArgonautSpec { 12 | def is = s2""" 13 | Predefined files can print and get same result $test 14 | """ 15 | 16 | val TestData = JsonFilesSpecBase.TestData 17 | 18 | def testData: collection.Seq[TestData] 19 | 20 | def test: Fragments = 21 | testData.map(testFile).reduce(_.append(_)) 22 | 23 | val baseDir = "./argonaut/jvm/src/test/resources/data" 24 | 25 | def testFile(t: TestData) = { 26 | val parsed = t.jsonString.parseOption 27 | val json = parsed.getOrElse(sys.error("could not parse json file [" + t.fileName + "]")) 28 | s2""" 29 | ${t.fileName} 30 | With no spaces ${json.nospaces.parseOption must beSome(json)} 31 | With 2 spaces ${json.spaces2.parseOption must beSome(json)} 32 | With 4 spaces ${json.spaces4.parseOption must beSome(json)}""" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/JsonNumberSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.scalacheck.* 4 | import Arbitrary.* 5 | import Data.* 6 | 7 | object JsonNumberSpecification extends ArgonautSpec { 8 | def is = s2""" 9 | fromString should 10 | Parse valid JSON number. $parseValidJsonNumbers 11 | Parse to Long when value fits in a Long. $longValuesProduceLongs 12 | Fail on empty string. $failOnEmptyString 13 | Fail on missing integer part. $failOnMissingInteger 14 | Fail on trailing decimal point. $failOnTrailingDecimal 15 | Fail on leading zero. $failOnLeadingZero 16 | Fail on trailing 'e'. $failOnTrailingE 17 | 18 | equals should 19 | Equivalent numbers are equal. $equivalentNumbersAreEqual 20 | 21 | truncateToBigInt should 22 | Fail on numbers with too many digits. $failOnLargeDecimalRepresentation 23 | """ 24 | 25 | def longValuesProduceLongs = prop { (value: Long) => 26 | JsonNumber.fromString(value.toString) must_== Some(JsonLong(value)) 27 | } 28 | 29 | def parseValidJsonNumbers = prop { (num: ValidJsonNumber) => 30 | val Some(x) = JsonNumber.fromString(num.value) 31 | 32 | x match { 33 | case JsonDecimal(value) => num.value must_== value 34 | case JsonLong(value) => num.value must_== value.toString 35 | } 36 | } 37 | 38 | def equivalentNumbersAreEqual = prop { (pair: EquivalentJsonNumberPair) => 39 | val EquivalentJsonNumberPair(lhs, rhs) = pair 40 | lhs must_== rhs 41 | } 42 | 43 | def failOnEmptyString = JsonNumber.fromString("") must beNone 44 | 45 | def failOnMissingInteger = { 46 | JsonNumber.fromString(".012e100") must beNone 47 | JsonNumber.fromString(".1234") must beNone 48 | } 49 | 50 | def failOnTrailingDecimal = { 51 | JsonNumber.fromString("0.") must beNone 52 | JsonNumber.fromString("-12.") must beNone 53 | JsonNumber.fromString("13.e-100") must beNone 54 | } 55 | 56 | def failOnLeadingZero = { 57 | JsonNumber.fromString("0123") must beNone 58 | JsonNumber.fromString("-001") must beNone 59 | } 60 | 61 | def failOnTrailingE = { 62 | JsonNumber.fromString("1e") must beNone 63 | JsonNumber.fromString("1e+") must beNone 64 | JsonNumber.fromString("1e-") must beNone 65 | JsonNumber.fromString("1E") must beNone 66 | JsonNumber.fromString("1E+") must beNone 67 | JsonNumber.fromString("1E-") must beNone 68 | } 69 | 70 | def failOnLargeDecimalRepresentation = { 71 | JsonNumber.fromString("1e262144").map(_.truncateToBigInt) must_== Some(None) 72 | JsonNumber.fromString("-1e262144").map(_.truncateToBigInt) must_== Some(None) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/JsonObjectSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Data.* 4 | 5 | object JsonObjectSpecification extends ArgonautSpec { 6 | def is = s2""" 7 | JsonObject 8 | fields ${prop((o: JsonObject) => o.fields.length == o.fieldSet.size)} 9 | """ 10 | } 11 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/JsonParserSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import org.scalacheck.Gen.* 4 | import org.scalacheck.Gen 5 | import org.scalacheck.Prop.* 6 | import org.scalacheck.Shrink.* 7 | import org.specs2.* 8 | import org.specs2.matcher.* 9 | import Data.* 10 | import Argonaut.* 11 | 12 | object JsonParserSpecification extends ArgonautSpec with DataTables with ScalaCheck { 13 | // Generates chunks of whitespace according to the not at all specified JSON specification. 14 | val whitespaceGen: Gen[String] = listOf(Gen.oneOf(' ', '\n', '\r', '\t')).map(_.mkString) 15 | 16 | val whitespaceObjectGen: Gen[String] = 17 | whitespaceGen.map(whitespace => """#{#"field1"#:#12#,#"field2"#:#"test"#}#""".replace("#", whitespace)) 18 | val whitespaceObject: Json = ("field1" := 12) ->: ("field2" := "test") ->: jEmptyObject 19 | 20 | val whitespaceArrayGen: Gen[String] = 21 | whitespaceGen.map(whitespace => """#[#"value1"#,#12#]#""".replace("#", whitespace)) 22 | val whitespaceArray: Json = jArray(jString("value1") :: jNumberOrNull(12) :: Nil) 23 | 24 | def is = s2""" 25 | parse 26 | Whitespace is handled correctly for an object $whitespaceForObject 27 | Whitespace is handled correctly for an array $whitespaceForArray 28 | Valid JSON parses into expected values $validJson 29 | Invalid JSON parses into expected failures $invalidJson 30 | Printed and then parsed again generates the same structure $printParse 31 | """ 32 | 33 | def whitespaceForObject = 34 | forAllNoShrink(whitespaceObjectGen) { json => 35 | val parseResult = JsonParser.parse(json) 36 | ("parseResult = " + parseResult) |: 37 | ("whitespaceObject = " + whitespaceObject) |: 38 | parseResult == Right(whitespaceObject) 39 | } 40 | 41 | def whitespaceForArray = 42 | forAllNoShrink(whitespaceArrayGen) { json => 43 | val parseResult = JsonParser.parse(json) 44 | ("parseResult = " + parseResult) |: 45 | ("whitespaceArray = " + whitespaceArray) |: 46 | parseResult == Right(whitespaceArray) 47 | } 48 | def validJson = 49 | KnownResults.validResultPairings |> { (json, expectedJSONValue) => 50 | val actualParseResult = JsonParser.parse(json) 51 | actualParseResult == Right(expectedJSONValue) 52 | } 53 | 54 | def invalidJson = 55 | KnownResults.parseFailures |> { (json, parseResult) => 56 | val actualParseResult = JsonParser.parse(json) 57 | actualParseResult == parseResult 58 | } 59 | 60 | def printParse = 61 | prop { (json: Json) => 62 | val printedJSON = json.nospaces 63 | ("printedJSON = " + printedJSON) |: { 64 | val parsed = printedJSON.parse 65 | ("parsed = " + parsed) |: parsed == Right(json) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/JsonSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import Data.* 4 | import Json.* 5 | import scalaz.syntax.std.option.* 6 | 7 | object JsonSpecification extends ArgonautSpec { 8 | 9 | def is = s2""" 10 | Json 11 | same value should be equal $sameValue 12 | modified string should not be equal $modString 13 | modified number should not be equal $modNumber 14 | modified array should not be equal $modArray 15 | modified object should not be equal $modObject 16 | modified boolean should not be equal $modBoolean 17 | not compose not is id $notComposeNot 18 | no-effect not equals !isBool $noEffect 19 | effect not equals isBool $effectNotIsBool 20 | effect withNumber implies isNumber $effectWithNumber 21 | effect withString implies isString $effectWithString 22 | effect withArray implies isArray $effectWithArray 23 | effect withObject implies isObject $effectWithObject 24 | Array prepend puts element on head $arrayPrepend 25 | jBool isBool $isBool 26 | jString isString $isString 27 | jArray isArray $isArray 28 | jSingleArray is single array $isSingleArray 29 | jObject isObject $isObject 30 | jSingleObject is single object $isSingleObject 31 | toString preserves order $toStringPreservesOrder 32 | """ 33 | 34 | def sameValue = prop((j: Json) => j == j) 35 | 36 | def modString = prop((j: JString) => j.withString(_ + "test") != j) 37 | 38 | def modNumber = prop((j: JNumber) => 39 | j.withNumber { number => 40 | JsonLong(number.toInt.map(n => if (n == 0) n + 1 else n * 2).getOrElse(0).toLong) 41 | } != j 42 | ) 43 | 44 | def modArray = prop((j: JArray) => j.withArray(jEmptyArray :: _) != j) 45 | 46 | def modObject = prop((j: JObject) => 47 | j.withObject(_ + ("veryunlikelytoberandomlygeneratedkey", jString("veryunlikelytoberandomlygeneratedvalue"))) != j 48 | ) 49 | 50 | def modBoolean = prop((j: JBool) => j.not != j) 51 | 52 | def notComposeNot = prop((j: Json) => j.not.not == j) 53 | 54 | def noEffect = prop((j: Json) => (j.not == j) != j.isBool) 55 | 56 | def effectNotIsBool = prop((j: Json) => (j.not != j) == j.isBool) 57 | 58 | def effectWithNumber = prop((j: Json, k: JsonNumber => JsonNumber) => ((j withNumber k) == j) || j.isNumber) 59 | 60 | def effectWithString = prop((j: Json, k: JsonString => JsonString) => ((j withString k) == j) || j.isString) 61 | 62 | def effectWithArray = prop((j: Json, k: JsonArray => JsonArray) => ((j withArray k) == j) || j.isArray) 63 | 64 | def effectWithObject = prop((j: Json, k: JsonObject => JsonObject) => ((j withObject k) == j) || j.isObject) 65 | 66 | def arrayPrepend = prop((j: Json, e: Json) => !j.isArray || (e -->>: j).array.map(_.head) == e.some) 67 | 68 | def isBool = prop((b: Boolean) => jBool(b).isBool) 69 | 70 | def isString = prop((s: String) => jString(s).isString) 71 | 72 | def isArray = prop((a: JsonArray) => jArray(a).isArray) 73 | 74 | def isSingleArray = prop((j: Json) => jSingleArray(j).array == List(j).some) 75 | 76 | def isObject = prop((a: JsonObject) => jObject(a).isObject) 77 | 78 | def isSingleObject = prop((f: JsonField, j: Json) => (jSingleObject(f, j).obj map (_.toList)) == List((f, j)).some) 79 | 80 | def toStringPreservesOrder = 81 | prop((j: Json) => PrettyParams.nospace.copy(preserveOrder = true).pretty(j) === j.toString) 82 | } 83 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/OptionParserSpecification.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import argonaut.Argonaut.* 4 | 5 | object OptionParserSpecification extends ArgonautSpec { 6 | def is = s2""" 7 | 8 | Option parsing when option is top level 9 | decoding empty string gives none $emptyString 10 | decoding malformed document gives none $malformed 11 | regular decoding is ok $regular 12 | 13 | Option parsing within a nested structure 14 | regular decoding of structure with option is ok $nestedWithNone 15 | regular decoding of structure with some is ok $nestedWithSome 16 | failing correctly when decoding nested document with wrong types $nestedIncorrectType 17 | failing correctly when nested document cannot be parsed $nestedInvalidDocument""" 18 | 19 | case class AnObject(c: String) 20 | case class OtherObject(a: Int, b: Option[AnObject]) 21 | 22 | implicit val codec2: CodecJson[AnObject] = CodecJson.derive[AnObject] 23 | implicit val codec1: CodecJson[OtherObject] = CodecJson.derive[OtherObject] 24 | 25 | def emptyString = "".decodeEither[AnObject] must beLeft 26 | 27 | def malformed = """{"foo": 1}""".decodeEither[AnObject] must beLeft 28 | def regular = """{"c": "Hello"}""".decodeOption[AnObject] must beSome(AnObject("Hello")) 29 | 30 | def nestedWithNone = """{"a": 1}""".decodeOption[OtherObject] must beSome(OtherObject(1, None)) 31 | def nestedWithSome = 32 | """{"a": 1, "b": {"c": "Hello"}}""".decodeOption[OtherObject] must beSome(OtherObject(1, Some(AnObject("Hello")))) 33 | def nestedIncorrectType = """{"a": 1, "b": {"c": 1}}""".decodeEither[OtherObject] must beLeft 34 | def nestedInvalidDocument = """{"a": 1, "b": {"c1": "1"}}""".decodeEither[OtherObject] must beLeft 35 | 36 | } 37 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/TestTypes.scala: -------------------------------------------------------------------------------- 1 | package argonaut 2 | 3 | import scalaz.* 4 | import org.scalacheck.* 5 | import Arbitrary.* 6 | 7 | case class Product(name: String, price: Double) 8 | case class OrderLine(product: Product, quantity: Int) 9 | case class Order(orderLines: Vector[OrderLine]) 10 | case class Person(name: String, age: Int, orders: Vector[Order], addressFields: Map[String, String]) 11 | 12 | sealed trait Shape 13 | case class Circle(radius: Int) extends Shape 14 | case class Square(side: Int) extends Shape 15 | 16 | case class BackTicks(`a.b.c`: String) 17 | 18 | case class Parameterized[A](value1: String, value2: A) 19 | 20 | object TestTypes { 21 | implicit def PersonEqual: Equal[Person] = Equal.equalA 22 | implicit def PersonShow: Show[Person] = Show.showFromToString 23 | implicit def BackTicksEqual: Equal[BackTicks] = Equal.equalA 24 | implicit def BackTicksShow: Show[BackTicks] = Show.showFromToString 25 | 26 | implicit def ProductArbitrary: Arbitrary[Product] = Arbitrary(for { 27 | n <- arbitrary[String] 28 | p <- arbitrary[Double] 29 | } yield Product(n, p)) 30 | 31 | implicit def OrderLineArbitrary: Arbitrary[OrderLine] = Arbitrary(for { 32 | p <- arbitrary[Product] 33 | q <- arbitrary[Int] 34 | } yield OrderLine(p, q)) 35 | 36 | implicit def OrderArbitrary: Arbitrary[Order] = Arbitrary(for { 37 | ol <- arbitrary[Vector[OrderLine]] 38 | } yield Order(ol)) 39 | 40 | implicit def PersonArbitrary: Arbitrary[Person] = Arbitrary(for { 41 | n <- arbitrary[String] 42 | a <- arbitrary[Int] 43 | o <- arbitrary[Vector[Order]] 44 | af <- arbitrary[Map[String, String]] 45 | } yield Person(n, a, o, af)) 46 | 47 | implicit def ShapeArbitrary: Arbitrary[Shape] = Arbitrary( 48 | Gen.oneOf( 49 | arbitrary[Int].map(Circle.apply), 50 | arbitrary[Int].map(Square.apply) 51 | ) 52 | ) 53 | 54 | implicit def BackticksArbitrary: Arbitrary[BackTicks] = Arbitrary(arbitrary[String].map(BackTicks.apply)) 55 | 56 | implicit def ParameterizedArbitrary[T: Arbitrary]: Arbitrary[Parameterized[T]] = Arbitrary(for { 57 | v1 <- arbitrary[String] 58 | v2 <- arbitrary[T] 59 | } yield Parameterized[T](v1, v2)) 60 | } 61 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/example/CodecExample.scala: -------------------------------------------------------------------------------- 1 | package argonaut.example 2 | 3 | import argonaut.* 4 | import Argonaut.* 5 | import scalaz.syntax.std.option.* 6 | 7 | object CodecExample extends ArgonautSpec { 8 | case class Person(name: String, age: Int) 9 | 10 | val fred = Person("Fred", 40) 11 | 12 | def encodeDecode(json: String)(implicit encode: EncodeJson[Person], decode: DecodeJson[Person]) = { 13 | val person: Option[Person] = json.decodeOption[Person] 14 | val encodedJson: Option[String] = person.map(_.jencode.nospaces) 15 | (person must be_==(fred.some)) and (encodedJson must be_==(json.some)) 16 | } 17 | 18 | def is = s2""" 19 | Array codec ${implicit val DecodePerson: DecodeJson[Person] = 20 | jdecode2(Person(_: String, _: Int)) 21 | 22 | implicit val EncodePerson: EncodeJson[Person] = 23 | jencode2((p: Person) => (p.name, p.age)) 24 | 25 | encodeDecode("""["Fred",40]""")} 26 | Object codec ${implicit val DecodePerson: DecodeJson[Person] = 27 | jdecode2L(Person(_: String, _: Int))("name", "age") 28 | 29 | implicit val EncodePerson: EncodeJson[Person] = 30 | jencode2L((p: Person) => (p.name, p.age))("name", "age") 31 | 32 | encodeDecode("""{"name":"Fred","age":40}""")} 33 | """ 34 | } 35 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/example/CursorExample.scala: -------------------------------------------------------------------------------- 1 | package argonaut.example 2 | 3 | import argonaut.* 4 | import Argonaut.* 5 | 6 | object CursorExample extends ArgonautSpec { 7 | val json = 8 | """ 9 | { 10 | "abc" : 11 | { 12 | "def" : 7 13 | }, 14 | "ghi" : 15 | { 16 | "ata" : null, 17 | "jkl" : 18 | { 19 | "mno" : "argo" 20 | } 21 | }, 22 | "pqr" : false, 23 | "operator": "is", 24 | "values": [ 25 | ["cat", "lol"] 26 | , "dog" 27 | , "rabbit" 28 | ], 29 | "xyz" : 24 30 | } 31 | """ 32 | 33 | def is = s2""" 34 | Replace '["cat", "lol"]' with 'false' ${json.parseOption flatMap (k => 35 | +k --\ "values" flatMap (_.downArray) map (_ := jBool(false)) map (-_) 36 | ) must beSome} 37 | Visit the 'values' array ${json.parseOption flatMap (k => +k --\ "values" flatMap (_.downArray) map (-_)) must beSome} 38 | Delete the element '"dog"' from the 'values' array. ${json.parseOption flatMap (k => 39 | +k --\ "values" flatMap (_.downArray) flatMap (_.right) flatMap (!_) map (-_) 40 | ) must beSome} 41 | Replace '["cat", "lol"]' with 'false' and '"rabbit"' with 'true' ${json.parseOption flatMap (k => 42 | +k --\ "values" flatMap (_.downArray) map (_ := jBool(false)) flatMap (_.right) flatMap (_.right) map (_ := jBool( 43 | true 44 | )) map (-_) 45 | ) must beSome} 46 | """ 47 | } 48 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/example/JsonExample.scala: -------------------------------------------------------------------------------- 1 | package argonaut.example 2 | 3 | import argonaut.TestCompat.* 4 | import argonaut.* 5 | import Argonaut.* 6 | 7 | object JsonExample extends ArgonautSpec { 8 | val json = 9 | Json( 10 | "name" := "fred", 11 | "age" := 23, 12 | "wallet" := List( 13 | Json { "value" := 100 }, 14 | Json { "value" := 10 }, 15 | Json { "value" := 50 } 16 | ) 17 | ) 18 | 19 | val value = 20 | Person("fred", 23, List(Coin(100), Coin(10), Coin(50))) 21 | 22 | case class Coin(value: Int) 23 | case class Person(name: String, age: Int, wallet: List[Coin]) 24 | 25 | implicit val CodecCoin: CodecJson[Coin] = casecodec1(Coin.apply, (a: Coin) => Option(a.value))("value") 26 | 27 | implicit val CodecPerson: CodecJson[Person] = 28 | casecodec3(Person.apply, (_: Person).asTuple)("name", "age", "wallet") 29 | 30 | def is = s2""" 31 | Can decode hand crafted object ${json.as[Person].toOption must beSome(value)} 32 | Can encode to match hand crafted object ${value.asJson must_== json} 33 | """ 34 | } 35 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/example/PolymorphicCursorExample.scala: -------------------------------------------------------------------------------- 1 | package argonaut.example 2 | 3 | import argonaut.* 4 | import Argonaut.* 5 | 6 | object PolymorphicCursorExample extends ArgonautSpec { 7 | 8 | sealed trait Animal extends scala.Product with Serializable 9 | case class Dog(name: String) extends Animal 10 | case class Cat(age: Int) extends Animal 11 | 12 | implicit def AnimalsCodecJson: CodecJson[Animal] = 13 | CodecJson( 14 | (a: Animal) => 15 | a match { 16 | case Dog(name) => ("type" := "dog") ->: ("name" := name) ->: jEmptyObject 17 | case Cat(age) => ("type" := "cat") ->: ("age" := age) ->: jEmptyObject 18 | }, 19 | c => 20 | for { 21 | klass <- (c --\ "type").as[String] 22 | result <- klass match { 23 | case "dog" => for { name <- (c --\ "name").as[String] } yield Dog(name) 24 | case "cat" => for { age <- (c --\ "age").as[Int] } yield Cat(age) 25 | } 26 | } yield result 27 | ) 28 | 29 | def is = s2""" 30 | Serialize class hierarchy is possible ${val dog: Animal = Dog("kutyus") 31 | val dogJson: String = dog.asJson.nospaces 32 | dogJson.decodeOption[Animal] must_== Some(dog)} 33 | Serialize multiple object is also possible ${val animals = List(Dog("dogy"), Cat(12), Dog("digy")) 34 | val animalsJson: String = animals.asJson.nospaces 35 | animalsJson.decodeOption[List[Animal]] must_== Some(animals)} 36 | """ 37 | } 38 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/example/PolymorphicHibridExample.scala: -------------------------------------------------------------------------------- 1 | package argonaut.example 2 | 3 | import argonaut.* 4 | import Argonaut.* 5 | 6 | object PolymorphicHibridExample extends ArgonautSpec { 7 | 8 | sealed trait Animal extends scala.Product with Serializable 9 | case class Dog(name: String) extends Animal 10 | case class Cat(age: Int) extends Animal 11 | 12 | val CodecDog = casecodec1(Dog.apply, (a: Dog) => Option(a.name))("name") // not implicit 13 | val CodecCat = casecodec1(Cat.apply, (a: Cat) => Option(a.age))("age") // not implicit 14 | 15 | implicit def AnimalsCodecJson: CodecJson[Animal] = 16 | CodecJson( 17 | (a: Animal) => 18 | a match { 19 | case dog @ Dog(_) => Json("type" -> jString("dog"), "value" -> dog.asJson(CodecDog.Encoder)) 20 | case cat @ Cat(_) => Json("type" -> jString("cat"), "value" -> cat.asJson(CodecCat.Encoder)) 21 | }, 22 | c => 23 | for { 24 | klass <- (c --\ "type").as[String] 25 | result <- klass match { 26 | case "dog" => for { value <- (c --\ "value").jdecode(CodecDog.Decoder) } yield value 27 | case "cat" => for { value <- (c --\ "value").jdecode(CodecCat.Decoder) } yield value 28 | } 29 | } yield result 30 | ) 31 | 32 | def is = s2""" 33 | Serialize class hierarchy is possible ${val dog: Animal = Dog("kutyus") 34 | val dogJson: String = dog.asJson.nospaces 35 | dogJson.decodeOption[Animal] must_== Some(dog)} 36 | Serialize multiple object is also possible ${val animals = List(Dog("dogy"), Cat(12), Dog("digy")) 37 | val animalsJson: String = animals.asJson.nospaces 38 | animalsJson.decodeOption[List[Animal]] must_== Some(animals)} 39 | """ 40 | } 41 | -------------------------------------------------------------------------------- /argonaut/shared/src/test/scala/argonaut/example/PrettyParamsExample.scala: -------------------------------------------------------------------------------- 1 | package argonaut.example 2 | 3 | import argonaut.TestCompat.* 4 | import argonaut.* 5 | import Argonaut.* 6 | 7 | object PrettyParamsExample extends ArgonautSpec { 8 | 9 | case class Address(street: String, number: Int, unit: Option[Int]) 10 | case class Person(name: String, age: Int, address: Option[Address], favouriteNumbers: Option[List[Int]]) 11 | 12 | implicit val AddressCodecJson: CodecJson[Address] = 13 | casecodec3(Address.apply, (_: Address).asTuple)("street", "number", "unit") 14 | 15 | implicit val PersonCodecJson: CodecJson[Person] = 16 | casecodec4(Person.apply, (_: Person).asTuple)("name", "age", "address", "favouriteNumbers") 17 | 18 | val person = Person("fred", 23, Some(Address("street", 123, None)), Some(List(1, 2, 3))) 19 | val personNoFavouriteNumbers = person.copy(favouriteNumbers = None) 20 | val personEmptyFavouriteNumbers = person.copy(favouriteNumbers = Some(Nil)) 21 | 22 | val prettyParams = PrettyParams.spaces2.copy(preserveOrder = true) 23 | 24 | val defaultPrettyParams2Spaces = prettyParams 25 | val defaultPrettyParams2SpacesJson = """ 26 | |{ 27 | | "name" : "fred", 28 | | "age" : 23, 29 | | "address" : { 30 | | "street" : "street", 31 | | "number" : 123, 32 | | "unit" : null 33 | | }, 34 | | "favouriteNumbers" : [ 35 | | 1, 36 | | 2, 37 | | 3 38 | | ] 39 | |} 40 | """.trim.stripMargin 41 | 42 | val defaultPrettyParams2SpacesNoSpaceBeforeColon = prettyParams.copy(colonLeft = "") 43 | val defaultPrettyParams2SpacesNoSpaceBeforeColonJson = """ 44 | |{ 45 | | "name": "fred", 46 | | "age": 23, 47 | | "address": { 48 | | "street": "street", 49 | | "number": 123, 50 | | "unit": null 51 | | }, 52 | | "favouriteNumbers": [ 53 | | 1, 54 | | 2, 55 | | 3 56 | | ] 57 | |} 58 | """.trim.stripMargin 59 | 60 | val prettyParamsDropNullKeys = prettyParams.copy(dropNullKeys = true) 61 | val prettyParamsDropNullKeysJson = """ 62 | |{ 63 | | "name" : "fred", 64 | | "age" : 23, 65 | | "address" : { 66 | | "street" : "street", 67 | | "number" : 123 68 | | } 69 | |} 70 | """.trim.stripMargin 71 | 72 | val prettyParamsArrayElementsOnSameLine = prettyParams.copy(arrayCommaRight = " ") 73 | val prettyParamsArrayElementsOnSameLineJson = """ 74 | |{ 75 | | "name" : "fred", 76 | | "age" : 23, 77 | | "address" : { 78 | | "street" : "street", 79 | | "number" : 123, 80 | | "unit" : null 81 | | }, 82 | | "favouriteNumbers" : [ 83 | | 1, 2, 3 84 | | ] 85 | |} 86 | """.trim.stripMargin 87 | 88 | val prettyParamsEmptyArray = prettyParams.copy(lrbracketsEmpty = "") 89 | val prettyParamsEmptyArrayJson = """ 90 | |{ 91 | | "name" : "fred", 92 | | "age" : 23, 93 | | "address" : { 94 | | "street" : "street", 95 | | "number" : 123, 96 | | "unit" : null 97 | | }, 98 | | "favouriteNumbers" : [] 99 | |} 100 | """.trim.stripMargin 101 | 102 | def is = s2""" 103 | Can print default pretty params with 2 spaces ${person.asJson.pretty( 104 | defaultPrettyParams2Spaces 105 | ) must_== defaultPrettyParams2SpacesJson} 106 | Can print default pretty params with 2 spaces no space before colon ${person.asJson.pretty( 107 | defaultPrettyParams2SpacesNoSpaceBeforeColon 108 | ) must_== defaultPrettyParams2SpacesNoSpaceBeforeColonJson} 109 | Can print default pretty params with 2 spaces with no null keys ${personNoFavouriteNumbers.asJson.pretty( 110 | prettyParamsDropNullKeys 111 | ) must_== prettyParamsDropNullKeysJson} 112 | Can print default pretty params with 2 spaces with array elements on the same line ${person.asJson.pretty( 113 | prettyParamsArrayElementsOnSameLine 114 | ) must_== prettyParamsArrayElementsOnSameLineJson} 115 | Can print default pretty params with 2 spaces with no gap for empty arrays ${personEmptyFavouriteNumbers.asJson 116 | .pretty(prettyParamsEmptyArray) must_== prettyParamsEmptyArrayJson} 117 | """ 118 | } 119 | -------------------------------------------------------------------------------- /notes/6.0-M2.markdown: -------------------------------------------------------------------------------- 1 | 2 | 6.0-M2 was primarily work on printing performance but a number 3 | of other small changes were made: 4 | 5 | * Significant performance improvements to pretty printing code. 6 | 7 | * Long and Int decoders re-instated support for decoding from both 8 | numbers and strings. 9 | 10 | * ParserWrap and Parser `Nel` methods were renamed to `Message`. 11 | This reflects the fact that the error is a single string and no 12 | longer an empty list. 13 | 14 | * Added Encode/Decode codecs for `Vector`. 15 | 16 | * Added specialised traversal operations on `HCursor`. 17 | 18 | * Added tail-recursive co-routine `loop` operation to `DecodeResult`. 19 | -------------------------------------------------------------------------------- /notes/6.0-M3.markdown: -------------------------------------------------------------------------------- 1 | 2 | 6.0-M3 was primarily to keep up with Scalaz 7.0.0-M9. 3 | 4 | * Added JsonArray lens thanks to @halcat0x15a 5 | 6 | * Fixed Cursor#rightN which could loop indefinitely 7 | 8 | * ACursor#focus new returns an option. Focus is `Some` 9 | only when `succeeded` is `true`. ACursor#focusFailed 10 | returns `Some` only when `succeeded is `false`. 11 | 12 | -------------------------------------------------------------------------------- /notes/6.0-M4.markdown: -------------------------------------------------------------------------------- 1 | 2 | 6.0-M4 was only to keep up with Scalaz 7.0.0-RC1. 3 | -------------------------------------------------------------------------------- /notes/6.0-M5.markdown: -------------------------------------------------------------------------------- 1 | 6.0-M5 contains a number of bug fixes and improvements. It a number 2 | of (minor) breaking changes, but they all have direct replacements. 3 | 4 | * Upgrade to scalaz-7.0.0 stable release. 5 | 6 | * Fix Attempt Cursor (`ACursor`) issues. Adresses #29, #35, #36. 7 | 8 | * Added unwind (`#unary_-` and `#undo` methods) to `ACursor` and 9 | `HCursor`. Addresses #30. 10 | 11 | * There are now convenient codec builders for tuples and case 12 | classes up to arity of 22. See `jencode{1..22}`, `jencode{1..22}L`, 13 | `jdecode{1..22}`, `jdecode{1..22}L`. Addresses #38, is a start on #19. 14 | 15 | * Internal tidy up of DecodeJson codecs. Removed some duplication, 16 | these are now a better example of how custom codecs should be 17 | defined. 18 | 19 | * Removed redundant JsonNumber wrapper, replaced with type alias. 20 | Addresses #31. 21 | 22 | * __note__: Any calls to `jDouble` convenience need to now directly call `jNumber` constructor. 23 | 24 | * Switched a number of types from traits with anonyomous implementations 25 | to case classes. Improved consistency of construction and default 26 | printing behaviour. 27 | 28 | * __note__: Any call to `ACursor` or `ACursor.apply`, now needs to call 29 | `ACursor.ok`. 30 | 31 | * __note__: Any call to `ACursor.failedACursor`, now needs to call 32 | `ACursor.fail`. 33 | 34 | * __note__: Any call to `ACursor` and `ACursor.apply`, can now be 35 | called with `HCursor \/ HCursor`. 36 | 37 | * __note__: Any call to `DecodeResult` or `DecodeResult.apply`, now needs to call 38 | `DecodeResult.ok`. 39 | 40 | * __note__: Any call to `DecodeResult.failedResult`, now needs to call 41 | `DecodeResult.fail`. 42 | 43 | * __note__: Any call to `DecodeResult` and `DecodeResult.apply`, can now be 44 | called with `(String, CursorHistory) \/ A`. 45 | 46 | * __note__: Any call to `CursorHistory` or `CursorHistory.apply`, now needs to call 47 | `ACursor.start`. 48 | -------------------------------------------------------------------------------- /notes/6.0-M6.markdown: -------------------------------------------------------------------------------- 1 | 6.0-M6 contains some major changes. 2 | 3 | * Major rework of `JsonObject` to elliminate pretty printing bottleneck, #43. 4 | 5 | * Introduced `CodecJson` to allow for `EncodeJson` and `DecodeJson` to 6 | be specified together. `CodecJson` includes a `laws` attribute to 7 | assist with testing codecs, #18, #41, #45. 8 | 9 | * Introduced `codec*` conveniences up to 22 for building `CodecJson` 10 | type class instances. 11 | 12 | * Introduced `casecodec*` conveniences up to 22 for building `CodecJson` 13 | type class instances for case classes. Note this relies on 14 | `_.unapply andThen (_.get)`. 15 | 16 | * Fixed major regression in implicit search for EncodeJson/DecodeJson 17 | caused by ambiguous Tuple1 instances. #44. 18 | -------------------------------------------------------------------------------- /notes/6.0-M7.markdown: -------------------------------------------------------------------------------- 1 | 6.0-M7 contains some minor bug fixes. 2 | 3 | * Remove implicit for derived `CodecJson`. Still available via CodecJson.derived. 4 | 5 | * Fixed val initialization in JsonObject, #46. 6 | -------------------------------------------------------------------------------- /notes/6.0-RC1.markdown: -------------------------------------------------------------------------------- 1 | 6.0-RC1 contains only minor improvements and fixes. 2 | 3 | * Add Json.{apply,obj,array} convenience builders after feedback 4 | on constructing ad-hoc structures. 5 | 6 | * Re-instated pretty printer lenses for building custom printers. See #27. 7 | 8 | * Pretty printer improvements to clean up printing of whole numbers. See #37. 9 | -------------------------------------------------------------------------------- /notes/6.0-RC2.markdown: -------------------------------------------------------------------------------- 1 | 6.0-RC2 contains a critical fix to the optional decoder and lots of performance improvements. 2 | 3 | * Parser performance improvements. See #47. 4 | 5 | * Printer performance improvements. See #48. 6 | 7 | * Fix option decoder. See #49 and markhibberd/argonaut.io#1. 8 | 9 | This includes a significant enhancement to DecodeJson to explicitly 10 | allow error handling on previously failed cursor navigation. This 11 | change should be source compatible with RC1 though. 12 | -------------------------------------------------------------------------------- /notes/6.0-RC3.markdown: -------------------------------------------------------------------------------- 1 | 6.0-RC3 contains a critical fix for an ambiguous implicit for DecodeJson[Json]. 2 | 3 | * Remove ambiguous implicit, and fix naming consistency for Json Encode/Decode. See #51. 4 | -------------------------------------------------------------------------------- /notes/6.0.1.markdown: -------------------------------------------------------------------------------- 1 | 6.0.1 RELEASE. 2 | 3 | * FIXes to cursor manipulation of Json Arrays. 4 | -------------------------------------------------------------------------------- /notes/6.0.2.markdown: -------------------------------------------------------------------------------- 1 | 6.0.2 RELEASE. 2 | 3 | * Added missing API operations to CursorOp, fixes #79. 4 | -------------------------------------------------------------------------------- /notes/6.0.3.markdown: -------------------------------------------------------------------------------- 1 | 6.0.3 RELEASE. 2 | 3 | * Support for scala 2.11.0-RC1, Fixes #54, #82. Thanks to @xuwei-k 4 | 5 | 6 | -------------------------------------------------------------------------------- /notes/6.0.4.markdown: -------------------------------------------------------------------------------- 1 | 6.0.4 RELEASE. 2 | 3 | * Support for scala 2.11, Fixes #94. Thanks to @xuwei-k 4 | -------------------------------------------------------------------------------- /notes/6.0.markdown: -------------------------------------------------------------------------------- 1 | 6.0 RELEASE. 2 | 3 | * Finally. 4 | 5 | * Identical to RC3. 6 | 7 | For full 6.0 release details see: 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | 18 | Special thanks to all the contributors that have helped out. 19 | * 20 | -------------------------------------------------------------------------------- /notes/6.1-M1.markdown: -------------------------------------------------------------------------------- 1 | 6.1-M1. 2 | 3 | * 7.1-SNAPSHOT of scalaz. 4 | -------------------------------------------------------------------------------- /notes/6.1-M2.markdown: -------------------------------------------------------------------------------- 1 | 6.1-M2. 2 | 3 | * 7.1.0-M3 of scalaz. 4 | -------------------------------------------------------------------------------- /notes/6.1-M3.markdown: -------------------------------------------------------------------------------- 1 | 6.1-M3. 2 | 3 | * 7.1.0-M6 of scalaz. 4 | -------------------------------------------------------------------------------- /notes/6.1-M4.markdown: -------------------------------------------------------------------------------- 1 | 6.1-M4. 2 | 3 | * 7.1.0 final of scalaz. 4 | * Monacle support thanks to @julien-truffaut 5 | * As a warning there are still plans for some incompatible changes before 6.1 final around numeric support. 6 | -------------------------------------------------------------------------------- /notes/6.1-M5.markdown: -------------------------------------------------------------------------------- 1 | 6.1-M5. 2 | 3 | * Massive reworking of number handling courtesy of @tixxit. 4 | * Improved pretty printing thanks to @guymers. 5 | -------------------------------------------------------------------------------- /notes/6.1-M6.markdown: -------------------------------------------------------------------------------- 1 | 6.1-M6. 2 | 3 | * Update of monocle library to 1.0.1. 4 | * Support for Maybe from the Scalaz library. 5 | -------------------------------------------------------------------------------- /notes/6.1.markdown: -------------------------------------------------------------------------------- 1 | 6.1. 2 | 3 | * Fix to Scaladoc linking thanks to @travisbrown. 4 | -------------------------------------------------------------------------------- /notes/6.2-M1.markdown: -------------------------------------------------------------------------------- 1 | 6.2-M1. 2 | 3 | * 7.2 release of scalaz. 4 | * Variance has been reintroduced back to types like EncodeJson and DecodeJson. 5 | * Argonaut now consists of several projects, with minimised dependencies. 6 | * Support for the cats library has been added. 7 | -------------------------------------------------------------------------------- /notes/about.markdown: -------------------------------------------------------------------------------- 1 | Argonaut is a JSON library for Scala. Argonaut 2 | emphasises composition, useful error-reporting and strong 3 | type-safety. Argonaut does NOT use reflection, for anything, ever. 4 | -------------------------------------------------------------------------------- /project/InfoSettings.scala: -------------------------------------------------------------------------------- 1 | import sbt.* 2 | import Keys.* 3 | 4 | object InfoSettings { 5 | type Sett = Def.Setting[?] 6 | 7 | def all = Seq[Sett](versioninfo) 8 | 9 | val versioninfo = (Compile / sourceGenerators) += task { 10 | val file = (Compile / sourceManaged).value / "info.scala" 11 | IO.write( 12 | file, 13 | s"""package argonaut 14 | |object Info { 15 | | val version = "${version.value}" 16 | | val name = "argonaut" 17 | |} 18 | |""".stripMargin 19 | ) 20 | Seq(file) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /project/PublishSettings.scala: -------------------------------------------------------------------------------- 1 | import sbt.* 2 | import Keys.* 3 | import com.jsuereth.sbtpgp.PgpKeys.* 4 | import sbtrelease.ReleasePlugin.autoImport.* 5 | 6 | object PublishSettings { 7 | type Sett = Def.Setting[?] 8 | 9 | lazy val all = Seq[Sett]( 10 | pom, 11 | publish, 12 | publishMavenStyle := true, 13 | Test / publishArtifact := false, 14 | pomIncludeRepository := { _ => false }, 15 | releasePublishArtifactsAction := publishSigned.value, 16 | releaseProcess := { 17 | import sbtrelease.ReleasePlugin.autoImport.ReleaseTransformations.* 18 | Seq[ReleaseStep]( 19 | checkSnapshotDependencies, 20 | inquireVersions, 21 | runTest, 22 | setReleaseVersion, 23 | commitReleaseVersion, 24 | tagRelease, 25 | releaseStepCommandAndRemaining("+ publishSigned"), 26 | releaseStepCommandAndRemaining("sonaRelease"), 27 | setNextVersion, 28 | commitNextVersion, 29 | pushChanges 30 | ) 31 | }, 32 | releaseCrossBuild := true, 33 | licenses := Seq("BSD-3-Clause" -> url("http://www.opensource.org/licenses/BSD-3-Clause")), 34 | homepage := Some(url("https://github.com/argonaut-io/argonaut")), 35 | autoAPIMappings := true, 36 | apiURL := Some(url("https://javadoc.io/doc/io.github.argonaut-io/argonaut_2.13/latest/argonaut/index.html")) 37 | ) 38 | 39 | lazy val pom: Sett = 40 | pomExtra := ( 41 | 42 | git@github.com:argonaut-io/argonaut.git 43 | scm:git:git@github.com:argonaut-io/argonaut.git 44 | 45 | 46 | 47 | tonymorris 48 | Tony Morris 49 | http://tmorris.net 50 | 51 | 52 | mth 53 | Mark Hibberd 54 | http://mth.io 55 | 56 | 57 | seanparsons 58 | Sean Parsons 59 | 60 | 61 | ) 62 | 63 | lazy val publish: Sett = 64 | publishTo := (if (isSnapshot.value) None else localStaging.value) 65 | } 66 | -------------------------------------------------------------------------------- /project/ReplSettings.scala: -------------------------------------------------------------------------------- 1 | import sbt.* 2 | import Keys.* 3 | 4 | object ReplSettings { 5 | type Sett = Def.Setting[?] 6 | 7 | lazy val all = Seq[Sett]( 8 | initialCommands := """ 9 | |import argonaut._ 10 | |import Argonaut._ 11 | """.stripMargin 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /project/ScalaSettings.scala: -------------------------------------------------------------------------------- 1 | import sbt.* 2 | import Keys.* 3 | 4 | object ScalaSettings { 5 | type Sett = Def.Setting[?] 6 | 7 | private[this] val unusedWarnings = Def.setting { 8 | Seq("-Ywarn-unused:imports") 9 | } 10 | 11 | def Scala212 = "2.12.20" 12 | def Scala213 = "2.13.17" 13 | def Scala3 = "3.3.6" 14 | 15 | lazy val all: Seq[Sett] = Def.settings( 16 | scalaVersion := Scala213, 17 | crossScalaVersions := Seq(Scala212, Scala213, Scala3), 18 | scalacOptions ++= { 19 | if (build.isScala3.value) { 20 | Seq( 21 | ) 22 | } else { 23 | Seq( 24 | unusedWarnings.value, 25 | Seq( 26 | "-Xlint" 27 | ) 28 | ).flatten 29 | } 30 | }, 31 | scalacOptions ++= Seq( 32 | "-deprecation", 33 | "-unchecked", 34 | "-feature", 35 | "-language:implicitConversions,higherKinds" 36 | ), 37 | scalacOptions ++= { 38 | if (scalaBinaryVersion.value == "2.13") { 39 | Seq("-Wconf:msg=method are copied from the case class constructor:silent") 40 | } else { 41 | Nil 42 | } 43 | }, 44 | scalacOptions ++= { 45 | CrossVersion.partialVersion(scalaVersion.value) match { 46 | case Some((2, _)) => 47 | Seq("-Xsource:3") 48 | case _ => 49 | Nil 50 | } 51 | }, 52 | scalacOptions ++= { 53 | CrossVersion.partialVersion(scalaVersion.value) match { 54 | case Some((2, 11 | 12)) => 55 | Seq("-Xfuture") 56 | case _ => 57 | Nil 58 | } 59 | } 60 | ) ++ Seq(Compile, Test).flatMap(c => (c / console / scalacOptions) --= unusedWarnings.value) 61 | } 62 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.7 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | scalacOptions += "-deprecation" 2 | 3 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") 4 | 5 | addSbtPlugin("com.github.sbt" % "sbt-release" % "1.4.0") 6 | 7 | addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4") 8 | 9 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1") 10 | 11 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") 12 | 13 | addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") 14 | 15 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.8") 16 | 17 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.5") 18 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | 3 | with pkgs; 4 | 5 | stdenv.mkDerivation { 6 | name = "argonaut"; 7 | 8 | buildInputs = [ 9 | simpleBuildTool 10 | nodejs 11 | ]; 12 | } 13 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | ThisBuild / version := "6.3.12-SNAPSHOT" 2 | --------------------------------------------------------------------------------