diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..807cb6c --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @freckle/backenders diff --git a/.github/workflows/add-asana-comment.yml b/.github/workflows/add-asana-comment.yml new file mode 100644 index 0000000..aaa3f6d --- /dev/null +++ b/.github/workflows/add-asana-comment.yml @@ -0,0 +1,16 @@ +name: Asana + +on: + pull_request: + types: [opened] + +jobs: + link-asana-task: + if: ${{ github.actor != 'dependabot[bot]' }} + runs-on: ubuntu-latest + steps: + - uses: Asana/create-app-attachment-github-action@v1.3 + id: postAttachment + with: + asana-secret: ${{ secrets.ASANA_API_ACCESS_KEY }} + - run: echo "Status is ${{ steps.postAttachment.outputs.status }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..477a784 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + pull_request: + push: + branches: main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + generate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - id: generate + uses: freckle/stack-action/generate-matrix@v5 + outputs: + stack-yamls: ${{ steps.generate.outputs.stack-yamls }} + + test: + needs: generate + runs-on: ubuntu-latest + + strategy: + matrix: + stack-yaml: ${{ fromJSON(needs.generate.outputs.stack-yamls) }} + fail-fast: false + + steps: + - uses: actions/checkout@v4 + - id: stack + uses: freckle/stack-action@v5 + env: + STACK_YAML: ${{ matrix.stack-yaml }} + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: haskell-actions/hlint-setup@v2 + - uses: haskell-actions/hlint-run@v2 + with: + fail-on: warning diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d0a6afa --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,20 @@ +name: Release + +on: + push: + branches: main + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - id: tag + uses: freckle/haskell-tag-action@v1 + + - if: steps.tag.outputs.tag + run: stack upload --pvp-bounds lower . + env: + HACKAGE_KEY: ${{ secrets.HACKAGE_UPLOAD_API_KEY }} + STACK_YAML: stack-lts21.yaml diff --git a/.github/workflows/restyled.yml b/.github/workflows/restyled.yml new file mode 100644 index 0000000..b54bdc6 --- /dev/null +++ b/.github/workflows/restyled.yml @@ -0,0 +1,18 @@ +name: Restyled + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + restyled: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: restyled-io/actions/setup@v4 + - uses: restyled-io/actions/run@v4 + with: + suggestions: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..271d433 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.hie +.hiedb +.stack-work +stack*.yaml.lock +.envrc +.direnv diff --git a/.restyled.yaml b/.restyled.yaml new file mode 100644 index 0000000..be7a0c0 --- /dev/null +++ b/.restyled.yaml @@ -0,0 +1,4 @@ +restylers: + - fourmolu + - "!stylish-haskell" + - "*" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5e3ed21 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +## [_Unreleased_](https://github.com/freckle/iri-orphans/compare/v0.0.0.0...main) + +## [v0.0.0.0](https://github.com/freckle/iri-orphans/tree/v0.0.0.0) + +First tagged release. diff --git a/README.md b/README.md index 47c9bc8..9743fc6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # iri-orphans + Haskell package defining orphan instances for types defined in the `iri` package diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..c33af0b --- /dev/null +++ b/flake.lock @@ -0,0 +1,571 @@ +{ + "nodes": { + "autodocodec": { + "flake": false, + "locked": { + "lastModified": 1739562847, + "narHash": "sha256-E5EnhdwNCQ4mwMI7rErFqoYLm3zFz1NgfUt6LzpTFPY=", + "owner": "NorfairKing", + "repo": "autodocodec", + "rev": "7439601de128dadf562430f4216cbdb28580ed20", + "type": "github" + }, + "original": { + "owner": "NorfairKing", + "repo": "autodocodec", + "type": "github" + } + }, + "fast-myers-diff": { + "flake": false, + "locked": { + "lastModified": 1736948652, + "narHash": "sha256-0iARqmAefoyDk+z1A5D2pyG9JEbdTCkI1zBrTwDviiU=", + "owner": "NorfairKing", + "repo": "fast-myers-diff", + "rev": "4a5c31884b8fc399085bffacd15d6cb5624b3177", + "type": "github" + }, + "original": { + "owner": "NorfairKing", + "repo": "fast-myers-diff", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "freckle": { + "inputs": { + "flake-utils": "flake-utils_2", + "haskell-openapi-code-generator": "haskell-openapi-code-generator", + "nix-github-actions": "nix-github-actions", + "nixpkgs-22-11": "nixpkgs-22-11", + "nixpkgs-23-05": "nixpkgs-23-05", + "nixpkgs-23-11": "nixpkgs-23-11", + "nixpkgs-24-05": "nixpkgs-24-05", + "nixpkgs-24-11": "nixpkgs-24-11", + "nixpkgs-haskell-updates": "nixpkgs-haskell-updates", + "nixpkgs-stable": "nixpkgs-stable", + "stack-lint-extra-deps": "stack-lint-extra-deps" + }, + "locked": { + "dir": "main", + "lastModified": 1745046164, + "narHash": "sha256-ffW/nUuo6nE6h0/WuX4KaGWijDRdvHKqzFyYQouvMBQ=", + "owner": "freckle", + "repo": "flakes", + "rev": "9355d795dabfc26f3490efd8f7e153364b311392", + "type": "github" + }, + "original": { + "dir": "main", + "owner": "freckle", + "repo": "flakes", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "freckle", + "haskell-openapi-code-generator", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "haskell-openapi-code-generator": { + "inputs": { + "autodocodec": "autodocodec", + "fast-myers-diff": "fast-myers-diff", + "flake-utils": "flake-utils_3", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks", + "safe-coloured-text": "safe-coloured-text", + "sydtest": "sydtest", + "validity": "validity" + }, + "locked": { + "lastModified": 1744629016, + "narHash": "sha256-EYf1ySZ5crofu1M28QphIDr9gEn+5DtIvvo4OVdcm3I=", + "owner": "Haskell-OpenAPI-Code-Generator", + "repo": "Haskell-OpenAPI-Client-Code-Generator", + "rev": "665374f465f214ffa766b5a43c6a6b8b921e4673", + "type": "github" + }, + "original": { + "owner": "Haskell-OpenAPI-Code-Generator", + "repo": "Haskell-OpenAPI-Client-Code-Generator", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "freckle", + "nixpkgs-stable" + ] + }, + "locked": { + "lastModified": 1737420293, + "narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1744440957, + "narHash": "sha256-FHlSkNqFmPxPJvy+6fNLaNeWnF1lZSgqVCl/eWaJRc4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "26d499fc9f1d567283d5d56fcf367edd815dba1d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-22-11": { + "locked": { + "lastModified": 1688392541, + "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-23-05": { + "locked": { + "lastModified": 1704290814, + "narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-23-11": { + "locked": { + "lastModified": 1720535198, + "narHash": "sha256-zwVvxrdIzralnSbcpghA92tWu2DV2lwv89xZc8MTrbg=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "205fd4226592cc83fd4c0885a3e4c9c400efabb5", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-24-05": { + "locked": { + "lastModified": 1735563628, + "narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-24-11": { + "locked": { + "lastModified": 1742751704, + "narHash": "sha256-rBfc+H1dDBUQ2mgVITMGBPI1PGuCznf9rcWX/XIULyE=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "f0946fa5f1fb876a9dc2e1850d9d3a4e3f914092", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-haskell-updates": { + "locked": { + "lastModified": 1744960667, + "narHash": "sha256-GhZOni4cMvmmcQ7gHhv7fq+5FUusD7DKb6CgupKDekA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c5dbc50d989180a50e0e371e10061d4f739a0511", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "haskell-updates", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-haskell-updates_2": { + "locked": { + "lastModified": 1742210880, + "narHash": "sha256-bFR9Tthdaz1mR/IzdtHgzPuw5nEHn49AWay8iUqFEPM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "1f00f46d8b14f6956005ab80f2e0e9c875d0dace", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "haskell-updates", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1736200483, + "narHash": "sha256-JO+lFN2HsCwSLMUWXHeOad6QUxOuwe9UOAF/iSl1J4I=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "3f0a8ac25fb674611b98089ca3a5dd6480175751", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1730768919, + "narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1742136038, + "narHash": "sha256-DDe16FJk18sadknQKKG/9FbwEro7A57tg9vB5kxZ8kY=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a1185f4064c18a5db37c5c84e5638c78b46e3341", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "pgp-wordlist": { + "flake": false, + "locked": { + "lastModified": 1714149562, + "narHash": "sha256-WCFQgtWqq+gLst4lXkFYlmlW7L8PQOJfChsKMApy3Ng=", + "owner": "quchen", + "repo": "pgp-wordlist", + "rev": "1f0cfd90d62179952cbfd59c3405283a1d364272", + "type": "github" + }, + "original": { + "owner": "quchen", + "repo": "pgp-wordlist", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1742649964, + "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "freckle": "freckle", + "stable": "stable" + } + }, + "safe-coloured-text": { + "flake": false, + "locked": { + "lastModified": 1728774106, + "narHash": "sha256-Xfv5izJtsdQ9MFhglkcEjJH4LlZ2m4Wmq+JJgMvnytc=", + "owner": "NorfairKing", + "repo": "safe-coloured-text", + "rev": "9ed65a40b3b2fade447c9d5fcb941d58f6367308", + "type": "github" + }, + "original": { + "owner": "NorfairKing", + "repo": "safe-coloured-text", + "type": "github" + } + }, + "stable": { + "locked": { + "lastModified": 1745279238, + "narHash": "sha256-AQ7M9wTa/Pa/kK5pcGTgX/DGqMHyzsyINfN7ktsI7Fo=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "9684b53175fc6c09581e94cc85f05ab77464c7e3", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "stack-lint-extra-deps": { + "inputs": { + "nixpkgs": "nixpkgs_3", + "nixpkgs-haskell-updates": "nixpkgs-haskell-updates_2", + "pgp-wordlist": "pgp-wordlist", + "stacklock2nix": "stacklock2nix" + }, + "locked": { + "lastModified": 1744218263, + "narHash": "sha256-DZg4hwzEe63oiaFRxyH2ek08cOVDrrFXpU2HzbtJE4I=", + "owner": "freckle", + "repo": "stack-lint-extra-deps", + "rev": "3ccabc07f47e244ee42d9841774739849d69cd78", + "type": "github" + }, + "original": { + "owner": "freckle", + "repo": "stack-lint-extra-deps", + "type": "github" + } + }, + "stacklock2nix": { + "locked": { + "lastModified": 1741332755, + "narHash": "sha256-ZQlzcx4f2cx4CQBQzMvEwJEAWJrfZEdZrGY7Iu5L9MA=", + "owner": "cdepillabout", + "repo": "stacklock2nix", + "rev": "47dd3fb2827fc0868142ab41a7daeaec0a6ddda1", + "type": "github" + }, + "original": { + "owner": "cdepillabout", + "repo": "stacklock2nix", + "type": "github" + } + }, + "sydtest": { + "flake": false, + "locked": { + "lastModified": 1742493430, + "narHash": "sha256-qATPlVpxHpnUe8n+EQlJyol/lh6RKoimVftHnCzUqFc=", + "owner": "NorfairKing", + "repo": "sydtest", + "rev": "0aad7d9c41a53f5d46694c87dcacc51f6c6063d5", + "type": "github" + }, + "original": { + "owner": "NorfairKing", + "repo": "sydtest", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "validity": { + "flake": false, + "locked": { + "lastModified": 1728772377, + "narHash": "sha256-5hHY8A8lO87G9TQLjwqCFzXmFMeXE71jeanuqHTS3T8=", + "owner": "NorfairKing", + "repo": "validity", + "rev": "512aea6e0a112f5df09cc0b08f7fceffb122abc6", + "type": "github" + }, + "original": { + "owner": "NorfairKing", + "repo": "validity", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..234e18c --- /dev/null +++ b/flake.nix @@ -0,0 +1,49 @@ +{ + inputs = { + stable.url = "github:nixos/nixpkgs/nixos-24.11"; + freckle.url = "github:freckle/flakes?dir=main"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = + inputs: + inputs.flake-utils.lib.eachDefaultSystem ( + system: + let + nixpkgsArgs = { + inherit system; + config = { }; + }; + + nixpkgs = { + stable = import inputs.stable nixpkgsArgs; + }; + freckle = inputs.freckle.packages.${system}; + freckleLib = inputs.freckle.lib.${system}; + + in + rec { + packages = { + fourmolu = freckle.fourmolu-0-17-x; + + ghc = freckleLib.haskellBundle { + ghcVersion = "ghc-9-8-4"; + enableHLS = true; + }; + }; + + devShells.default = nixpkgs.stable.mkShell { + buildInputs = with (nixpkgs.stable); [ zlib ]; + + nativeBuildInputs = with (packages); [ + fourmolu + ghc + nixpkgs.stable.nixfmt-rfc-style + ]; + + shellHook = '' + export STACK_YAML=stack.yaml + ''; + }; + } + ); +} diff --git a/fourmolu.yaml b/fourmolu.yaml new file mode 100644 index 0000000..811d473 --- /dev/null +++ b/fourmolu.yaml @@ -0,0 +1,37 @@ +indentation: 2 +column-limit: 80 # needs fourmolu >= v0.12 +function-arrows: leading +comma-style: leading # default +import-export-style: leading +import-grouping: # needs fourmolu >= v0.17 + - name: "Preludes" + rules: + - glob: Prelude + - glob: "**.Prelude" + - glob: Import + - glob: "**.Import" + - glob: TestImport + - glob: "**.TestImport" + - glob: "TestImport.**" + - glob: "TestPrelude" + - glob: "**.TestPrelude" + - name: "Everything else" + rules: + - match: all + priority: 100 +indent-wheres: false # default +record-brace-space: true +newlines-between-decls: 1 # default +haddock-style: single-line +let-style: mixed +in-style: left-align +single-constraint-parens: never # needs fourmolu >= v0.12 +sort-constraints: true # needs fourmolu >= v0.17 +sort-derived-classes: true # needs fourmolu >= v0.17 +sort-derived-clauses: true # needs fourmolu >= v0.17 +trailing-section-operators: false # needs fourmolu >= v0.12 +unicode: never # default +respectful: true # default +fixities: + - "infix 4 `stringEqual`" + - "infixl 1 &" diff --git a/iri-orphans.cabal b/iri-orphans.cabal new file mode 100644 index 0000000..665858b --- /dev/null +++ b/iri-orphans.cabal @@ -0,0 +1,147 @@ +cabal-version: 1.18 + +-- This file has been generated from package.yaml by hpack version 0.37.0. +-- +-- see: https://github.com/sol/hpack + +name: iri-orphans +version: 0.0.0.0 +synopsis: Orphan instances for iri +description: Orphan instances for types defined in the `iri` package. +category: Utils +homepage: https://github.com/freckle/iri-orphans#readme +bug-reports: https://github.com/freckle/iri-orphans/issues +maintainer: Freckle Education +license: MIT +license-file: LICENSE +build-type: Simple +extra-source-files: + package.yaml +extra-doc-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://github.com/freckle/iri-orphans + +library + exposed-modules: + Iri.Orphans + Iri.Orphans.Aeson + Iri.Orphans.Aeson.FromJSON + Iri.Orphans.Aeson.ToJSON + Iri.Orphans.Autodocodec + Iri.Orphans.Autodocodec.HasCodec + Iri.Orphans.DebugPrint + Iri.Orphans.DebugPrint.ToDebugPrintValue + Iri.Orphans.OpenApi + Iri.Orphans.OpenApi.ToSchema + Iri.Orphans.Persistent + Iri.Orphans.Persistent.PersistField + Iri.Orphans.Persistent.PersistFieldSql + Iri.Orphans.QuickCheck + Iri.Orphans.QuickCheck.Arbitrary + Iri.Orphans.Serialise + Iri.Orphans.Serialise.Serialise + other-modules: + Paths_iri_orphans + hs-source-dirs: + src + default-extensions: + ApplicativeDo + BangPatterns + BlockArguments + DataKinds + DerivingStrategies + DerivingVia + FlexibleContexts + FlexibleInstances + GADTs + GeneralizedNewtypeDeriving + LambdaCase + MultiParamTypeClasses + NoImplicitPrelude + NoMonomorphismRestriction + OverloadedStrings + RankNTypes + RecordWildCards + ScopedTypeVariables + StandaloneDeriving + TypeApplications + TypeFamilies + ghc-options: -Weverything -Wno-all-missed-specialisations -Wno-missed-specialisations -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-safe -Wno-unsafe + build-depends: + QuickCheck + , aeson + , autodocodec + , base <5 + , debug-print + , generic-arbitrary + , ip + , iri + , lens + , openapi3 + , persistent + , serialise + , text + , vector + , wide-word + default-language: GHC2021 + if impl(ghc >= 9.8) + ghc-options: -Wno-missing-role-annotations -Wno-missing-poly-kind-signatures + if impl(ghc >= 9.2) + ghc-options: -Wno-missing-kind-signatures + if impl(ghc >= 8.10) + ghc-options: -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module + if impl(ghc >= 8.8) + ghc-options: -fwrite-ide-info + +test-suite spec + type: exitcode-stdio-1.0 + main-is: Spec.hs + other-modules: + Iri.Orphans.QuickCheck.ArbitrarySpec + SpecHook + Paths_iri_orphans + hs-source-dirs: + test + default-extensions: + ApplicativeDo + BangPatterns + BlockArguments + DataKinds + DerivingStrategies + DerivingVia + FlexibleContexts + FlexibleInstances + GADTs + GeneralizedNewtypeDeriving + LambdaCase + MultiParamTypeClasses + NoImplicitPrelude + NoMonomorphismRestriction + OverloadedStrings + RankNTypes + RecordWildCards + ScopedTypeVariables + StandaloneDeriving + TypeApplications + TypeFamilies + ghc-options: -Weverything -Wno-all-missed-specialisations -Wno-missed-specialisations -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-safe -Wno-unsafe -threaded -rtsopts "-with-rtsopts=-N" + build-depends: + QuickCheck + , base <5 + , hspec + , hspec-junit-formatter + , iri + , iri-orphans + default-language: GHC2021 + if impl(ghc >= 9.8) + ghc-options: -Wno-missing-role-annotations -Wno-missing-poly-kind-signatures + if impl(ghc >= 9.2) + ghc-options: -Wno-missing-kind-signatures + if impl(ghc >= 8.10) + ghc-options: -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module + if impl(ghc >= 8.8) + ghc-options: -fwrite-ide-info diff --git a/package.yaml b/package.yaml new file mode 100644 index 0000000..ae5f179 --- /dev/null +++ b/package.yaml @@ -0,0 +1,100 @@ +name: iri-orphans +version: 0.0.0.0 +maintainer: Freckle Education +category: Utils +github: freckle/iri-orphans +synopsis: Orphan instances for iri +description: | + Orphan instances for types defined in the `iri` package. + +extra-doc-files: + - README.md + - CHANGELOG.md + +extra-source-files: + - package.yaml + +ghc-options: + - -Weverything + - -Wno-all-missed-specialisations + - -Wno-missed-specialisations + - -Wno-missing-exported-signatures # re-enables missing-signatures + - -Wno-missing-import-lists + - -Wno-missing-local-signatures + - -Wno-monomorphism-restriction + - -Wno-safe + - -Wno-unsafe + +when: + - condition: "impl(ghc >= 9.8)" + ghc-options: + - -Wno-missing-role-annotations + - -Wno-missing-poly-kind-signatures + - condition: "impl(ghc >= 9.2)" + ghc-options: + - -Wno-missing-kind-signatures + - condition: "impl(ghc >= 8.10)" + ghc-options: + - -Wno-missing-safe-haskell-mode + - -Wno-prepositive-qualified-module + - condition: "impl(ghc >= 8.8)" + ghc-options: + - -fwrite-ide-info + +dependencies: + - base < 5 + +language: GHC2021 + +default-extensions: + - ApplicativeDo + - BangPatterns + - BlockArguments + - DataKinds + - DerivingStrategies + - DerivingVia + - FlexibleContexts + - FlexibleInstances + - GADTs + - GeneralizedNewtypeDeriving + - LambdaCase + - MultiParamTypeClasses + - NoImplicitPrelude + - NoMonomorphismRestriction + - OverloadedStrings + - RankNTypes + - RecordWildCards + - ScopedTypeVariables + - StandaloneDeriving + - TypeApplications + - TypeFamilies + +library: + source-dirs: src + dependencies: + - aeson + - autodocodec + - debug-print + - generic-arbitrary + - ip + - iri + - lens + - openapi3 + - persistent + - QuickCheck + - serialise + - text + - vector + - wide-word + +tests: + spec: + main: Spec.hs + source-dirs: test + ghc-options: -threaded -rtsopts "-with-rtsopts=-N" + dependencies: + - hspec + - hspec-junit-formatter + - iri + - iri-orphans + - QuickCheck diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..8efcb83 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["local>freckle/renovate-config"], + "minimumReleaseAge": "0 days" +} diff --git a/src/Iri/Orphans.hs b/src/Iri/Orphans.hs new file mode 100644 index 0000000..0ea66bf --- /dev/null +++ b/src/Iri/Orphans.hs @@ -0,0 +1,9 @@ +module Iri.Orphans () where + +import Iri.Orphans.Aeson () +import Iri.Orphans.Autodocodec () +import Iri.Orphans.DebugPrint () +import Iri.Orphans.OpenApi () +import Iri.Orphans.Persistent () +import Iri.Orphans.QuickCheck () +import Iri.Orphans.Serialise () diff --git a/src/Iri/Orphans/Aeson.hs b/src/Iri/Orphans/Aeson.hs new file mode 100644 index 0000000..997f573 --- /dev/null +++ b/src/Iri/Orphans/Aeson.hs @@ -0,0 +1,4 @@ +module Iri.Orphans.Aeson () where + +import Iri.Orphans.Aeson.FromJSON () +import Iri.Orphans.Aeson.ToJSON () diff --git a/src/Iri/Orphans/Aeson/FromJSON.hs b/src/Iri/Orphans/Aeson/FromJSON.hs new file mode 100644 index 0000000..0b1ba1d --- /dev/null +++ b/src/Iri/Orphans/Aeson/FromJSON.hs @@ -0,0 +1,10 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.Aeson.FromJSON () where + +import Autodocodec (Autodocodec (..)) +import Data.Aeson (FromJSON) +import Iri.Data (HttpIri) +import Iri.Orphans.Autodocodec.HasCodec () + +deriving via Autodocodec HttpIri instance FromJSON HttpIri diff --git a/src/Iri/Orphans/Aeson/ToJSON.hs b/src/Iri/Orphans/Aeson/ToJSON.hs new file mode 100644 index 0000000..de7ec26 --- /dev/null +++ b/src/Iri/Orphans/Aeson/ToJSON.hs @@ -0,0 +1,10 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.Aeson.ToJSON () where + +import Autodocodec (Autodocodec (..)) +import Data.Aeson (ToJSON) +import Iri.Data (HttpIri) +import Iri.Orphans.Autodocodec.HasCodec () + +deriving via Autodocodec HttpIri instance ToJSON HttpIri diff --git a/src/Iri/Orphans/Autodocodec.hs b/src/Iri/Orphans/Autodocodec.hs new file mode 100644 index 0000000..02572ce --- /dev/null +++ b/src/Iri/Orphans/Autodocodec.hs @@ -0,0 +1,3 @@ +module Iri.Orphans.Autodocodec () where + +import Iri.Orphans.Autodocodec.HasCodec () diff --git a/src/Iri/Orphans/Autodocodec/HasCodec.hs b/src/Iri/Orphans/Autodocodec/HasCodec.hs new file mode 100644 index 0000000..e2b3fff --- /dev/null +++ b/src/Iri/Orphans/Autodocodec/HasCodec.hs @@ -0,0 +1,20 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.Autodocodec.HasCodec () where + +import Prelude + +import Autodocodec (HasCodec (codec), bimapCodec) +import Data.Bifunctor (first) +import Data.Text (Text) +import Data.Text qualified as T +import Iri.Data (HttpIri) +import Iri.Parsing.Text qualified +import Iri.Rendering.Text qualified + +instance HasCodec HttpIri where + codec = + bimapCodec + (first T.unpack . Iri.Parsing.Text.httpIri) + Iri.Rendering.Text.httpIri + (codec @Text) diff --git a/src/Iri/Orphans/DebugPrint.hs b/src/Iri/Orphans/DebugPrint.hs new file mode 100644 index 0000000..28d5f9b --- /dev/null +++ b/src/Iri/Orphans/DebugPrint.hs @@ -0,0 +1,3 @@ +module Iri.Orphans.DebugPrint () where + +import Iri.Orphans.DebugPrint.ToDebugPrintValue () diff --git a/src/Iri/Orphans/DebugPrint/ToDebugPrintValue.hs b/src/Iri/Orphans/DebugPrint/ToDebugPrintValue.hs new file mode 100644 index 0000000..2b24743 --- /dev/null +++ b/src/Iri/Orphans/DebugPrint/ToDebugPrintValue.hs @@ -0,0 +1,14 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.DebugPrint.ToDebugPrintValue () where + +import Prelude + +import Data.Text (Text) +import DebugPrint (ToDebugPrintValue (..)) +import Iri.Data (HttpIri) +import Iri.Rendering.Text qualified + +instance ToDebugPrintValue HttpIri where + toDebugPrintValue = + toDebugPrintValue @Text . Iri.Rendering.Text.httpIri diff --git a/src/Iri/Orphans/OpenApi.hs b/src/Iri/Orphans/OpenApi.hs new file mode 100644 index 0000000..332e454 --- /dev/null +++ b/src/Iri/Orphans/OpenApi.hs @@ -0,0 +1,3 @@ +module Iri.Orphans.OpenApi () where + +import Iri.Orphans.OpenApi.ToSchema () diff --git a/src/Iri/Orphans/OpenApi/ToSchema.hs b/src/Iri/Orphans/OpenApi/ToSchema.hs new file mode 100644 index 0000000..c3069b6 --- /dev/null +++ b/src/Iri/Orphans/OpenApi/ToSchema.hs @@ -0,0 +1,15 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.OpenApi.ToSchema () where + +import Prelude + +import Control.Lens ((&), (?~)) +import Data.OpenApi +import Iri.Data (HttpIri) + +instance ToSchema HttpIri where + declareNamedSchema _ = + pure + $ NamedSchema (Just "HttpIri") + $ mempty & type_ ?~ OpenApiString diff --git a/src/Iri/Orphans/Persistent.hs b/src/Iri/Orphans/Persistent.hs new file mode 100644 index 0000000..6dca94e --- /dev/null +++ b/src/Iri/Orphans/Persistent.hs @@ -0,0 +1,4 @@ +module Iri.Orphans.Persistent () where + +import Iri.Orphans.Persistent.PersistField () +import Iri.Orphans.Persistent.PersistFieldSql () diff --git a/src/Iri/Orphans/Persistent/PersistField.hs b/src/Iri/Orphans/Persistent/PersistField.hs new file mode 100644 index 0000000..26d0432 --- /dev/null +++ b/src/Iri/Orphans/Persistent/PersistField.hs @@ -0,0 +1,16 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.Persistent.PersistField () where + +import Prelude + +import Control.Monad ((<=<)) +import Data.Text (Text) +import Database.Persist +import Iri.Data (HttpIri) +import Iri.Parsing.Text qualified +import Iri.Rendering.Text qualified + +instance PersistField HttpIri where + toPersistValue = toPersistValue @Text . Iri.Rendering.Text.httpIri + fromPersistValue = Iri.Parsing.Text.httpIri <=< fromPersistValue @Text diff --git a/src/Iri/Orphans/Persistent/PersistFieldSql.hs b/src/Iri/Orphans/Persistent/PersistFieldSql.hs new file mode 100644 index 0000000..1a36cac --- /dev/null +++ b/src/Iri/Orphans/Persistent/PersistFieldSql.hs @@ -0,0 +1,14 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.Persistent.PersistFieldSql () where + +import Prelude + +import Data.Proxy +import Data.Text (Text) +import Database.Persist.Sql +import Iri.Data (HttpIri) +import Iri.Orphans.Persistent.PersistField () + +instance PersistFieldSql HttpIri where + sqlType _ = sqlType $ Proxy @Text diff --git a/src/Iri/Orphans/QuickCheck.hs b/src/Iri/Orphans/QuickCheck.hs new file mode 100644 index 0000000..5ab3d94 --- /dev/null +++ b/src/Iri/Orphans/QuickCheck.hs @@ -0,0 +1,3 @@ +module Iri.Orphans.QuickCheck () where + +import Iri.Orphans.QuickCheck.Arbitrary () diff --git a/src/Iri/Orphans/QuickCheck/Arbitrary.hs b/src/Iri/Orphans/QuickCheck/Arbitrary.hs new file mode 100644 index 0000000..01c4e67 --- /dev/null +++ b/src/Iri/Orphans/QuickCheck/Arbitrary.hs @@ -0,0 +1,142 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.QuickCheck.Arbitrary () where + +import Prelude + +import Data.Text qualified as T +import Data.Text.Encoding qualified as T +import Data.Vector qualified as V +import Iri.Data + ( DomainLabel (..) + , Fragment (..) + , Host (..) + , HttpIri + , Path (..) + , PathSegment (..) + , Port + , Query (..) + , RegName (..) + , Security (..) + ) +import Test.QuickCheck + ( Arbitrary + , Gen + , arbitrary + , choose + , elements + , listOf + , listOf1 + , oneof + ) +import Test.QuickCheck.Arbitrary.Generic + ( GenericArbitrary + , genericArbitrary + ) +import Test.QuickCheck.Arbitrary.Generic qualified + +instance Arbitrary DomainLabel where + arbitrary = DomainLabel . T.pack <$> listOf1 char + where + char = + oneof + [ choose ('a', 'z') + , choose ('A', 'Z') + , choose ('0', '9') + , elements "-_~!$&'()*+,;=" + ] + +instance Arbitrary Fragment where + arbitrary = Fragment . T.encodeUtf8 . T.pack <$> listOf char + where + char = + oneof + [ choose ('a', 'z') + , choose ('A', 'Z') + , choose ('0', '9') + , elements "-._~:@/?|" + , ucs + ] + +instance Arbitrary Host where + -- Don't generate an IPv6 host because + -- https://github.com/nikita-volkov/iri/issues/2 + arbitrary = + oneof + [ NamedHost <$> arbitrary + , IpV4Host <$> genericArbitrary + ] + +deriving via + GenericArbitrary HttpIri + instance + Arbitrary HttpIri + +instance Arbitrary Path where + arbitrary = Path . V.fromList <$> listOf arbitrary + +instance Arbitrary PathSegment where + arbitrary = PathSegment . T.encodeUtf8 . T.pack <$> listOf1 char + where + char = + oneof + [ choose ('a', 'z') + , choose ('A', 'Z') + , choose ('0', '9') + , elements "-._~:@" + , ucs + ] + +deriving via + GenericArbitrary Port + instance + Arbitrary Port + +instance Arbitrary Query where + arbitrary = Query . T.encodeUtf8 . T.pack <$> listOf1 char + where + char = + oneof + [ choose ('a', 'z') + , choose ('A', 'Z') + , choose ('0', '9') + , elements "-._~:@/?|" + , ucs + , private + ] + +instance Arbitrary RegName where + arbitrary = RegName . V.fromList <$> listOf1 arbitrary + +deriving newtype instance + Arbitrary Security + +ucs :: Gen Char +ucs = + oneof + [ choose ('\xA0', '\xD7FF') + , choose ('\xF900', '\xFDCF') + , choose ('\xFDF0', '\xFFEF') + , choose ('\x10000', '\x1FFFD') + , choose ('\x20000', '\x2FFFD') + , choose ('\x30000', '\x3FFFD') + , choose ('\x40000', '\x4FFFD') + , choose ('\x50000', '\x5FFFD') + , choose ('\x60000', '\x6FFFD') + , choose ('\x70000', '\x7FFFD') + , choose ('\x80000', '\x8FFFD') + , choose ('\x90000', '\x9FFFD') + , choose ('\xA0000', '\xAFFFD') + , choose ('\xB0000', '\xBFFFD') + , choose ('\xC0000', '\xCFFFD') + , choose ('\xD0000', '\xDFFFD') + , choose ('\xE1000', '\xEFFFD') + ] + +private :: Gen Char +private = + oneof + [ choose ('\xE000', '\xF8FF') + , choose ('\xF0000', '\xFFFFD') + , choose ('\x100000', '\x10FFFD') + ] diff --git a/src/Iri/Orphans/Serialise.hs b/src/Iri/Orphans/Serialise.hs new file mode 100644 index 0000000..f3fe1f3 --- /dev/null +++ b/src/Iri/Orphans/Serialise.hs @@ -0,0 +1,3 @@ +module Iri.Orphans.Serialise () where + +import Iri.Orphans.Serialise.Serialise () diff --git a/src/Iri/Orphans/Serialise/Serialise.hs b/src/Iri/Orphans/Serialise/Serialise.hs new file mode 100644 index 0000000..5d83abf --- /dev/null +++ b/src/Iri/Orphans/Serialise/Serialise.hs @@ -0,0 +1,23 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Iri.Orphans.Serialise.Serialise () where + +import Codec.Serialise (Serialise) +import Data.WideWord.Word128 (Word128) +import Iri.Data +import Net.IPv4 (IPv4) +import Net.IPv6 (IPv6) + +instance Serialise DomainLabel +instance Serialise Fragment +instance Serialise Host +instance Serialise HttpIri +instance Serialise IPv4 +instance Serialise IPv6 +instance Serialise Path +instance Serialise PathSegment +instance Serialise Port +instance Serialise Query +instance Serialise RegName +instance Serialise Security +instance Serialise Word128 diff --git a/stack-lts21.yaml b/stack-lts21.yaml new file mode 100644 index 0000000..292a908 --- /dev/null +++ b/stack-lts21.yaml @@ -0,0 +1,14 @@ +resolver: lts-21.25 + +extra-deps: + - debug-print-0.2.0.1 + - hspec-api-2.11.10 + - hspec-2.11.10 + - hspec-core-2.11.10 + - hspec-discover-2.11.10 + - hspec-expectations-0.8.4 + - hspec-junit-formatter-1.1.2.1 + - ip-1.7.8 + - iri-0.5.1.1 + - text-builder-1.0.0.3 + - text-builder-core-0.1.1.1 diff --git a/stack-lts22.yaml b/stack-lts22.yaml new file mode 100644 index 0000000..385e619 --- /dev/null +++ b/stack-lts22.yaml @@ -0,0 +1,7 @@ +resolver: lts-22.43 + +extra-deps: + - debug-print-0.2.0.1 + - iri-0.5.1.1 + - text-builder-1.0.0.3 + - text-builder-core-0.1.1.1 diff --git a/stack-lts23.yaml b/stack-lts23.yaml new file mode 100644 index 0000000..63e1898 --- /dev/null +++ b/stack-lts23.yaml @@ -0,0 +1,7 @@ +resolver: lts-23.19 + +extra-deps: + - debug-print-0.2.0.1 + - iri-0.5.1.1 + - text-builder-1.0.0.3 + - text-builder-core-0.1.1.1 diff --git a/stack-nightly.yaml b/stack-nightly.yaml new file mode 100644 index 0000000..b7754e7 --- /dev/null +++ b/stack-nightly.yaml @@ -0,0 +1,7 @@ +resolver: nightly-2025-04-24 + +extra-deps: + - debug-print-0.2.0.1 + - iri-0.5.1.1 + - text-builder-1.0.0.3 + - text-builder-core-0.1.1.1 diff --git a/stack.yaml b/stack.yaml new file mode 120000 index 0000000..d7de56b --- /dev/null +++ b/stack.yaml @@ -0,0 +1 @@ +stack-lts23.yaml \ No newline at end of file diff --git a/test/Iri/Orphans/QuickCheck/ArbitrarySpec.hs b/test/Iri/Orphans/QuickCheck/ArbitrarySpec.hs new file mode 100644 index 0000000..595203d --- /dev/null +++ b/test/Iri/Orphans/QuickCheck/ArbitrarySpec.hs @@ -0,0 +1,30 @@ +module Iri.Orphans.QuickCheck.ArbitrarySpec + ( spec + ) where + +import Prelude + +import Data.List (intercalate) +import Iri.Data (HttpIri (..)) +import Iri.Orphans.QuickCheck () +import Iri.Parsing.Text qualified +import Iri.Rendering.Text qualified +import Test.Hspec +import Test.QuickCheck (counterexample, property) + +spec :: Spec +spec = + it "generates only parseable values" $ property $ \x -> + counterexample + ( let HttpIri security host port path query fragment = x + in intercalate + ", " + [ show security + , show host + , show port + , show path + , show query + , show fragment + ] + ) + $ Iri.Parsing.Text.httpIri (Iri.Rendering.Text.httpIri x) == Right x diff --git a/test/Spec.hs b/test/Spec.hs new file mode 100644 index 0000000..8044961 --- /dev/null +++ b/test/Spec.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover -Wno-missing-export-lists #-} diff --git a/test/SpecHook.hs b/test/SpecHook.hs new file mode 100644 index 0000000..e064a69 --- /dev/null +++ b/test/SpecHook.hs @@ -0,0 +1,11 @@ +module SpecHook + ( hook + ) where + +import Prelude + +import Test.Hspec +import Test.Hspec.JUnit.Formatter.Env as Formatter + +hook :: Spec -> Spec +hook = Formatter.whenEnabled Formatter.add . parallel