git.iko.soy/typeable/http-api-data
on af01cff help sign in
.ghcinew file+1
since splitsnapshots
unifiedsplit
@@ -0,0 +1 @@
1+:set -itest -isrc -optP-include -optP .stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/autogen/cabal_macros.h -optP-I -optPinclude
.github/workflows/haskell-ci.ymldeleted−283
@@ -1,283 +0,0 @@
1−# This GitHub workflow config has been generated by a script via
2−#
3−# haskell-ci 'github' 'cabal.project'
4−#
5−# To regenerate the script (for example after adjusting tested-with) run
6−#
7−# haskell-ci regenerate
8−#
9−# For more information, see https://github.com/haskell-CI/haskell-ci
10−#
11−# version: 0.15.20220609
12−#
13−# REGENDATA ("0.15.20220609",["github","cabal.project"])
14−#
15−name: Haskell-CI
16−on:
17− push:
18− branches:
19− - master
20− pull_request:
21− branches:
22− - master
23−jobs:
24− linux:
25− name: Haskell-CI - Linux - ${{ matrix.compiler }}
26− runs-on: ubuntu-20.04
27− timeout-minutes:
28− 60
29− container:
30− image: buildpack-deps:bionic
31− continue-on-error: ${{ matrix.allow-failure }}
32− strategy:
33− matrix:
34− include:
35− - compiler: ghc-9.4.0.20220523
36− compilerKind: ghc
37− compilerVersion: 9.4.0.20220523
38− setup-method: ghcup
39− allow-failure: true
40− - compiler: ghc-9.2.3
41− compilerKind: ghc
42− compilerVersion: 9.2.3
43− setup-method: ghcup
44− allow-failure: false
45− - compiler: ghc-9.0.2
46− compilerKind: ghc
47− compilerVersion: 9.0.2
48− setup-method: ghcup
49− allow-failure: false
50− - compiler: ghc-8.10.7
51− compilerKind: ghc
52− compilerVersion: 8.10.7
53− setup-method: ghcup
54− allow-failure: false
55− - compiler: ghc-8.8.4
56− compilerKind: ghc
57− compilerVersion: 8.8.4
58− setup-method: hvr-ppa
59− allow-failure: false
60− - compiler: ghc-8.6.5
61− compilerKind: ghc
62− compilerVersion: 8.6.5
63− setup-method: hvr-ppa
64− allow-failure: false
65− - compiler: ghc-8.4.4
66− compilerKind: ghc
67− compilerVersion: 8.4.4
68− setup-method: hvr-ppa
69− allow-failure: false
70− - compiler: ghc-8.2.2
71− compilerKind: ghc
72− compilerVersion: 8.2.2
73− setup-method: hvr-ppa
74− allow-failure: false
75− - compiler: ghc-8.0.2
76− compilerKind: ghc
77− compilerVersion: 8.0.2
78− setup-method: hvr-ppa
79− allow-failure: false
80− fail-fast: false
81− steps:
82− - name: apt
83− run: |
84− apt-get update
85− apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
86− if [ "${{ matrix.setup-method }}" = ghcup ]; then
87− mkdir -p "$HOME/.ghcup/bin"
88− curl -sL https://downloads.haskell.org/ghcup/0.1.17.8/x86_64-linux-ghcup-0.1.17.8 > "$HOME/.ghcup/bin/ghcup"
89− chmod a+x "$HOME/.ghcup/bin/ghcup"
90− if $HEADHACKAGE; then "$HOME/.ghcup/bin/ghcup" config add-release-channel https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.7.yaml; fi
91− "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
92− "$HOME/.ghcup/bin/ghcup" install cabal 3.6.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
93− else
94− apt-add-repository -y 'ppa:hvr/ghc'
95− apt-get update
96− apt-get install -y "$HCNAME"
97− mkdir -p "$HOME/.ghcup/bin"
98− curl -sL https://downloads.haskell.org/ghcup/0.1.17.8/x86_64-linux-ghcup-0.1.17.8 > "$HOME/.ghcup/bin/ghcup"
99− chmod a+x "$HOME/.ghcup/bin/ghcup"
100− "$HOME/.ghcup/bin/ghcup" install cabal 3.6.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
101− fi
102− env:
103− HCKIND: ${{ matrix.compilerKind }}
104− HCNAME: ${{ matrix.compiler }}
105− HCVER: ${{ matrix.compilerVersion }}
106− - name: Set PATH and environment variables
107− run: |
108− echo "$HOME/.cabal/bin" >> $GITHUB_PATH
109− echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
110− echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
111− echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
112− HCDIR=/opt/$HCKIND/$HCVER
113− if [ "${{ matrix.setup-method }}" = ghcup ]; then
114− HC=$HOME/.ghcup/bin/$HCKIND-$HCVER
115− echo "HC=$HC" >> "$GITHUB_ENV"
116− echo "HCPKG=$HOME/.ghcup/bin/$HCKIND-pkg-$HCVER" >> "$GITHUB_ENV"
117− echo "HADDOCK=$HOME/.ghcup/bin/haddock-$HCVER" >> "$GITHUB_ENV"
118− echo "CABAL=$HOME/.ghcup/bin/cabal-3.6.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
119− else
120− HC=$HCDIR/bin/$HCKIND
121− echo "HC=$HC" >> "$GITHUB_ENV"
122− echo "HCPKG=$HCDIR/bin/$HCKIND-pkg" >> "$GITHUB_ENV"
123− echo "HADDOCK=$HCDIR/bin/haddock" >> "$GITHUB_ENV"
124− echo "CABAL=$HOME/.ghcup/bin/cabal-3.6.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
125− fi
126−
127− HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
128− echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
129− echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
130− echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
131− if [ $((HCNUMVER >= 90400)) -ne 0 ] ; then echo "HEADHACKAGE=true" >> "$GITHUB_ENV" ; else echo "HEADHACKAGE=false" >> "$GITHUB_ENV" ; fi
132− echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
133− echo "GHCJSARITH=0" >> "$GITHUB_ENV"
134− env:
135− HCKIND: ${{ matrix.compilerKind }}
136− HCNAME: ${{ matrix.compiler }}
137− HCVER: ${{ matrix.compilerVersion }}
138− - name: env
139− run: |
140− env
141− - name: write cabal config
142− run: |
143− mkdir -p $CABAL_DIR
144− cat >> $CABAL_CONFIG <<EOF
145− remote-build-reporting: anonymous
146− write-ghc-environment-files: never
147− remote-repo-cache: $CABAL_DIR/packages
148− logs-dir: $CABAL_DIR/logs
149− world-file: $CABAL_DIR/world
150− extra-prog-path: $CABAL_DIR/bin
151− symlink-bindir: $CABAL_DIR/bin
152− installdir: $CABAL_DIR/bin
153− build-summary: $CABAL_DIR/logs/build.log
154− store-dir: $CABAL_DIR/store
155− install-dirs user
156− prefix: $CABAL_DIR
157− repository hackage.haskell.org
158− url: http://hackage.haskell.org/
159− EOF
160− if $HEADHACKAGE; then
161− cat >> $CABAL_CONFIG <<EOF
162− repository head.hackage.ghc.haskell.org
163− url: https://ghc.gitlab.haskell.org/head.hackage/
164− secure: True
165− root-keys: 7541f32a4ccca4f97aea3b22f5e593ba2c0267546016b992dfadcd2fe944e55d
166− 26021a13b401500c8eb2761ca95c61f2d625bfef951b939a8124ed12ecf07329
167− f76d08be13e9a61a377a85e2fb63f4c5435d40f8feb3e12eb05905edb8cdea89
168− key-threshold: 3
169− active-repositories: hackage.haskell.org, head.hackage.ghc.haskell.org:override
170− EOF
171− fi
172− cat >> $CABAL_CONFIG <<EOF
173− program-default-options
174− ghc-options: $GHCJOBS +RTS -M3G -RTS
175− EOF
176− cat $CABAL_CONFIG
177− - name: versions
178− run: |
179− $HC --version || true
180− $HC --print-project-git-commit-id || true
181− $CABAL --version || true
182− - name: update cabal index
183− run: |
184− $CABAL v2-update -v
185− - name: cache (tools)
186− uses: actions/cache@v2
187− with:
188− key: ${{ runner.os }}-${{ matrix.compiler }}-tools-445c2459
189− path: ~/.haskell-ci-tools
190− - name: install cabal-plan
191− run: |
192− mkdir -p $HOME/.cabal/bin
193− curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.6.2.0/cabal-plan-0.6.2.0-x86_64-linux.xz > cabal-plan.xz
194− echo 'de73600b1836d3f55e32d80385acc055fd97f60eaa0ab68a755302685f5d81bc cabal-plan.xz' | sha256sum -c -
195− xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
196− rm -f cabal-plan.xz
197− chmod a+x $HOME/.cabal/bin/cabal-plan
198− cabal-plan --version
199− - name: install doctest
200− run: |
201− if [ $((HCNUMVER < 90000)) -ne 0 ] ; then $CABAL --store-dir=$HOME/.haskell-ci-tools/store v2-install $ARG_COMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.20' ; fi
202− if [ $((HCNUMVER < 90000)) -ne 0 ] ; then doctest --version ; fi
203− - name: checkout
204− uses: actions/checkout@v2
205− with:
206− path: source
207− - name: initial cabal.project for sdist
208− run: |
209− touch cabal.project
210− echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project
211− cat cabal.project
212− - name: sdist
213− run: |
214− mkdir -p sdist
215− $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist
216− - name: unpack
217− run: |
218− mkdir -p unpacked
219− find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \;
220− - name: generate cabal.project
221− run: |
222− PKGDIR_http_api_data="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/http-api-data-[0-9.]*')"
223− echo "PKGDIR_http_api_data=${PKGDIR_http_api_data}" >> "$GITHUB_ENV"
224− rm -f cabal.project cabal.project.local
225− touch cabal.project
226− touch cabal.project.local
227− echo "packages: ${PKGDIR_http_api_data}" >> cabal.project
228− if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo "package http-api-data" >> cabal.project ; fi
229− if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo " ghc-options: -Werror=missing-methods" >> cabal.project ; fi
230− cat >> cabal.project <<EOF
231− EOF
232− if $HEADHACKAGE; then
233− echo "allow-newer: $($HCPKG list --simple-output | sed -E 's/([a-zA-Z-]+)-[0-9.]+/*:\1,/g')" >> cabal.project
234− fi
235− $HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(http-api-data)$/; }' >> cabal.project.local
236− cat cabal.project
237− cat cabal.project.local
238− - name: dump install plan
239− run: |
240− $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
241− cabal-plan
242− - name: cache
243− uses: actions/cache@v2
244− with:
245− key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
246− path: ~/.cabal/store
247− restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
248− - name: install dependencies
249− run: |
250− $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
251− $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
252− - name: build w/o tests
253− run: |
254− $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
255− - name: build
256− run: |
257− $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always
258− - name: tests
259− run: |
260− $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
261− - name: doctest
262− run: |
263− perl -i -e 'while (<ARGV>) { print unless /package-id\s+(base-compat)-\d+(\.\d+)*/; }' .ghc.environment.*
264− if [ $((HCNUMVER < 90000)) -ne 0 ] ; then cd ${PKGDIR_http_api_data} || false ; fi
265− if [ $((HCNUMVER < 90000)) -ne 0 ] ; then doctest -XHaskell2010 src/ ; fi
266− - name: cabal check
267− run: |
268− cd ${PKGDIR_http_api_data} || false
269− ${CABAL} -vnormal check
270− - name: haddock
271− run: |
272− $CABAL v2-haddock --haddock-all $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
273− - name: unconstrained build
274− run: |
275− rm -f cabal.project.local
276− $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
277− - name: prepare for constraint sets
278− run: |
279− rm -f cabal.project.local
280− - name: constraint set text-show
281− run: |
282− $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='http-api-data +use-text-show' --dependencies-only -j2 all
283− $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='http-api-data +use-text-show' all
.travis.ymlnew file+123
@@ -0,0 +1,123 @@
1+# This Travis job script has been generated by a script via
2+#
3+# runghc make_travis_yml_2.hs '--config' '--output' '.travis.yml' 'http-api-data.cabal'
4+#
5+# For more information, see https://github.com/haskell-CI/haskell-ci
6+#
7+language: c
8+sudo: false
9+
10+git:
11+ submodules: false # whether to recursively clone submodules
12+
13+branches:
14+ only:
15+ - master
16+
17+cache:
18+ directories:
19+ - $HOME/.cabal/packages
20+ - $HOME/.cabal/store
21+
22+before_cache:
23+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
24+ # remove files that are regenerated by 'cabal update'
25+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
26+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
27+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
28+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
29+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
30+
31+ - rm -rfv $HOME/.cabal/packages/head.hackage
32+
33+matrix:
34+ include:
35+ - compiler: "ghc-8.6.1"
36+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
37+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.1], sources: [hvr-ghc]}}
38+ - compiler: "ghc-8.4.3"
39+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
40+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.3], sources: [hvr-ghc]}}
41+ - compiler: "ghc-8.2.2"
42+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
43+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}
44+ - compiler: "ghc-8.0.2"
45+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
46+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}}
47+ - compiler: "ghc-7.10.3"
48+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
49+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}}
50+ - compiler: "ghc-7.8.4"
51+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
52+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}}
53+
54+before_install:
55+ - HC=${CC}
56+ - HCPKG=${HC/ghc/ghc-pkg}
57+ - unset CC
58+ - ROOTDIR=$(pwd)
59+ - mkdir -p $HOME/.local/bin
60+ - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
61+ - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
62+ - echo $HCNUMVER
63+
64+install:
65+ - cabal --version
66+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
67+ - BENCH=${BENCH---enable-benchmarks}
68+ - TEST=${TEST---enable-tests}
69+ - HADDOCK=${HADDOCK-true}
70+ - UNCONSTRAINED=${UNCONSTRAINED-true}
71+ - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}
72+ - GHCHEAD=${GHCHEAD-false}
73+ - travis_retry cabal update -v
74+ - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
75+ - rm -fv cabal.project cabal.project.local
76+ - "if [ $HCNUMVER -ge 70800 ]; then sed -i.bak 's/-- ghc-options:.*/ghc-options: -j2/' ${HOME}/.cabal/config; fi"
77+ - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
78+ - "printf 'packages: \".\"\\n' > cabal.project"
79+ - touch cabal.project.local
80+ - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- http-api-data | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
81+ - cat cabal.project || true
82+ - cat cabal.project.local || true
83+ - if [ -f "./configure.ac" ]; then
84+ (cd "." && autoreconf -i);
85+ fi
86+ - rm -f cabal.project.freeze
87+ - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
88+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
89+ - rm -rf .ghc.environment.* "."/dist
90+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
91+
92+# Here starts the actual work to be performed for the package under test;
93+# any command which exits with a non-zero exit code causes the build to fail.
94+script:
95+ # test that source-distributions can be generated
96+ - (cd "." && cabal sdist)
97+ - mv "."/dist/http-api-data-*.tar.gz ${DISTDIR}/
98+ - cd ${DISTDIR} || false
99+ - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
100+ - "printf 'packages: http-api-data-*/*.cabal\\n' > cabal.project"
101+ - touch cabal.project.local
102+ - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- http-api-data | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
103+ - cat cabal.project || true
104+ - cat cabal.project.local || true
105+ # this builds all libraries and executables (without tests/benchmarks)
106+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
107+
108+ # build & run tests, build benchmarks
109+ - cabal new-build -w ${HC} ${TEST} ${BENCH} all
110+ - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi
111+
112+ # cabal check
113+ - (cd http-api-data-* && cabal check)
114+
115+ # haddock
116+ - rm -rf ./dist-newstyle
117+ - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
118+
119+ # Build without installed constraints for packages in global-db
120+ - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi
121+
122+# REGENDATA ["--config","--output",".travis.yml","http-api-data.cabal"]
123+# EOF
CHANGELOG.md+2−46
@@ -1,54 +1,10 @@
1−0.5
2−---
3−
4−* Use `attoparsec-iso8601-1.1.0.0`.
5− `Day` and `UTCTime` parsers require at least 4 digits now, which is a breaking change.
6−* Drop GHC-7.8 and GHC-7.10 support.
7−
8−0.4.3
9−-----
10−
11−* Add `Quarter`, `QuarterOfYear` and `Month` instances
12−* Support `bytestring-0.11`
13−
14−0.4.2
15−-----
16−
17−* Add instances for `Const` and `Identity`
18−
19−0.4.1.1
20−-------
21−
22−* Allow cookie <0.5
23−* Change to `build-type: Simple`
24−
25−0.4.1
26−-----
27−
28−* Use `time-compat` to provide instances for `DayOfWeek`.
29−
30−0.4
31−---
32−
33−* `NominalDiffTime` instances preserve precision (roundtrip)
34−* Add `Semigroup.Min`, `Max`, `First`, `Last` instances
35−* Add `Tagged b a` instances
36−
37−0.3.10
38−---
39−
40−* Fix 'SetCookie' instances
41− (see [#86](https://github.com/fizruk/http-api-data/pull/86))
42−* Add support for `Fixed`
43− (see [#78](https://github.com/fizruk/http-api-data/pull/87))
44−
450.3.9
10.3.9
46---
2---
47
3
48* GHC-8.6 support
4* GHC-8.6 support
49* Remove dependency on `uri-bytestring` and use functions from `http-types` instead
5* Remove dependency on `uri-bytestring` and use functions from `http-types` instead
50− (see [#75](https://github.com/fizruk/http-api-data/pull/75))
6+ (see [#75](https://github.com/fizruk/http-api-data/pull/78))
51−* Add support for `SetCookie`
7+* Add support for `Cookie`
52 (see [#74](https://github.com/fizruk/http-api-data/pull/74))
8 (see [#74](https://github.com/fizruk/http-api-data/pull/74))
53
9
540.3.8.1
100.3.8.1
Setup.hsdeleted−2
@@ -1,2 +0,0 @@
1−import Distribution.Simple
2−main = defaultMain
Setup.lhsnew file+36
@@ -0,0 +1,36 @@
1+\begin{code}
2+{-# LANGUAGE CPP #-}
3+{-# OPTIONS_GHC -Wall #-}
4+module Main (main) where
5+
6+#ifndef MIN_VERSION_cabal_doctest
7+#define MIN_VERSION_cabal_doctest(x,y,z) 0
8+#endif
9+
10+#if MIN_VERSION_cabal_doctest(1,0,0)
11+
12+import Distribution.Extra.Doctest ( defaultMainWithDoctests )
13+main :: IO ()
14+main = defaultMainWithDoctests "doctests"
15+
16+#else
17+
18+#ifdef MIN_VERSION_Cabal
19+-- If the macro is defined, we have new cabal-install,
20+-- but for some reason we don't have cabal-doctest in package-db
21+--
22+-- Probably we are running cabal sdist, when otherwise using new-build
23+-- workflow
24+#warning You are configuring this package without cabal-doctest installed. \
25+ The doctests test-suite will not work as a result. \
26+ To fix this, install cabal-doctest before configuring.
27+#endif
28+
29+import Distribution.Simple
30+
31+main :: IO ()
32+main = defaultMain
33+
34+#endif
35+
36+\end{code}
cabal.haskell-cideleted−6
@@ -1,6 +0,0 @@
1−branches: master
2−doctest: <9
3−doctest-filter-packages: base-compat
4−
5−constraint-set text-show
6− constraints: http-api-data +use-text-show
cabal.make-travis-ymlnew file+2
@@ -0,0 +1,2 @@
1+jobs: :2
2+branches: master
cabal.projectdeleted−2
@@ -1,2 +0,0 @@
1−packages: .
2−tests: True
http-api-data.cabal+48−37
@@ -1,6 +1,6 @@
1cabal-version: >= 1.10
1cabal-version: >= 1.10
2name: http-api-data
2name: http-api-data
3−version: 0.5
3+version: 0.3.9
4
4
5synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters.
5synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters.
6category: Web
6category: Web
@@ -15,24 +15,26 @@ author: Nickolay Kudasov <nickolay.kudasov@gmail.com>
15maintainer: Nickolay Kudasov <nickolay.kudasov@gmail.com>
15maintainer: Nickolay Kudasov <nickolay.kudasov@gmail.com>
16homepage: http://github.com/fizruk/http-api-data
16homepage: http://github.com/fizruk/http-api-data
17stability: unstable
17stability: unstable
18−build-type: Simple
18+build-type: Custom
19
19
20extra-source-files:
20extra-source-files:
21 include/overlapping-compat.h
21 include/overlapping-compat.h
22 test/*.hs
22 test/*.hs
23 CHANGELOG.md
23 CHANGELOG.md
24 README.md
24 README.md
25−
26tested-with:
25tested-with:
26+ GHC==7.8.4,
27+ GHC==7.10.3,
27 GHC==8.0.2,
28 GHC==8.0.2,
28 GHC==8.2.2,
29 GHC==8.2.2,
29− GHC==8.4.4,
30+ GHC==8.4.3,
30− GHC==8.6.5,
31+ GHC==8.6.1
31− GHC==8.8.4,
32+
32− GHC==8.10.7,
33+custom-setup
33− GHC==9.0.2,
34+ setup-depends:
34− GHC==9.2.3,
35+ base,
35− GHC==9.4.1
36+ Cabal,
37+ cabal-doctest >=1.0.6 && <1.1
36
38
37flag use-text-show
39flag use-text-show
38 description: Use text-show library for efficient ToHttpApiData implementations.
40 description: Use text-show library for efficient ToHttpApiData implementations.
@@ -44,28 +46,29 @@ library
44 include-dirs: include/
46 include-dirs: include/
45
47
46 -- GHC bundled
48 -- GHC bundled
47− build-depends: base >= 4.9 && < 4.17
49+ build-depends: base >= 4.7 && < 4.13
48− , bytestring >= 0.10.8.1 && < 0.12
50+ , bytestring >= 0.10.4.0 && < 0.11
49− , containers >= 0.5.7.1 && < 0.7
51+ , containers >= 0.5.5.1 && < 0.7
50− , text >= 1.2.3.0 && < 1.3 || >=2.0 && <2.1
52+ , text >= 1.2.3.0 && < 1.3
51− , transformers >= 0.5.2.0 && < 0.7
53+ , time >= 1.4.2 && < 1.9
52
54
53 -- other-dependencies
55 -- other-dependencies
54 build-depends:
56 build-depends:
55− attoparsec >= 0.13.2.2 && < 0.15
57+ attoparsec >= 0.13.2.2 && < 0.14
56− , attoparsec-iso8601 >= 1.1.0.0 && < 1.2
58+ , attoparsec-iso8601 >= 1.0.0.0 && < 1.1
57− , base-compat >= 0.10.5 && < 0.13
58− , cookie >= 0.4.3 && < 0.5
59+ , cookie >= 0.4.3 && < 0.4.5
59− , hashable >= 1.2.7.0 && < 1.5
60+ , hashable >= 1.2.7.0 && < 1.3
60− , http-types >= 0.12.3 && < 0.13
61+ , http-types >= 0.12.1 && < 0.13
61− , tagged >= 0.8.5 && < 0.9
62− , time-compat >= 1.9.5 && < 1.10
62+ , time-locale-compat >= 0.1.1.5 && < 0.2
63− , unordered-containers >= 0.2.10.0 && < 0.3
63+ , unordered-containers >= 0.2.9.0 && < 0.3
64− , uuid-types >= 1.0.3 && < 1.1
64+ , uuid-types >= 1.0.3 && <1.1
65+
66+ if !impl(ghc >= 8.0)
67+ build-depends: semigroups >= 0.18.5 && < 0.19
65
68
66 if flag(use-text-show)
69 if flag(use-text-show)
67 cpp-options: -DUSE_TEXT_SHOW
70 cpp-options: -DUSE_TEXT_SHOW
68− build-depends: text-show >= 3.8.2 && <3.10
71+ build-depends: text-show >= 3.7.4 && <3.8
69
72
70 exposed-modules:
73 exposed-modules:
71 Web.HttpApiData
74 Web.HttpApiData
@@ -85,26 +88,34 @@ test-suite spec
85 hs-source-dirs: test
88 hs-source-dirs: test
86 ghc-options: -Wall
89 ghc-options: -Wall
87 default-language: Haskell2010
90 default-language: Haskell2010
88− build-tool-depends: hspec-discover:hspec-discover >= 2.7.1 && <2.11
91+ build-tool-depends: hspec-discover:hspec-discover >= 2.5.5 && <2.6
89 -- inherited depndencies
92 -- inherited depndencies
90 build-depends:
93 build-depends:
91 base
94 base
92− , base-compat
93 , bytestring
95 , bytestring
94− , cookie
96+ , unordered-containers
95 , http-api-data
97 , http-api-data
96 , text
98 , text
97− , time-compat
99+ , time
98− , unordered-containers
100+ , bytestring
99 , uuid-types
101 , uuid-types
100
102
101− if !impl(ghc >= 7.10)
102− build-depends: nats
103+ build-depends: HUnit >= 1.6.0.0 && <1.7
104+ , hspec >= 2.5.5 && <2.6
105+ , QuickCheck >= 2.11.3 && <2.13
106+ , quickcheck-instances >= 0.3.19 && <0.4
103
107
108+test-suite doctests
109+ ghc-options: -Wall
104− build-depends: HUnit >= 1.6.0.0 && <1.7
110+ build-depends:
105− , hspec >= 2.7.1 && <2.11
111+ base,
106− , QuickCheck >= 2.13.1 && <2.15
112+ directory >= 1.0,
107− , quickcheck-instances >= 0.3.25.2 && <0.4
113+ doctest >= 0.16.0 && <0.17,
114+ filepath
115+ default-language: Haskell2010
116+ hs-source-dirs: test
117+ main-is: doctests.hs
118+ type: exitcode-stdio-1.0
108
119
109source-repository head
120source-repository head
110 type: git
121 type: git
src/Web/HttpApiData.hs+2−1
@@ -49,9 +49,10 @@ import Web.Internal.HttpApiData
49--
49--
50-- >>> :set -XOverloadedStrings
50-- >>> :set -XOverloadedStrings
51-- >>> import Control.Applicative
51-- >>> import Control.Applicative
52−-- >>> import Data.Time.Compat
52+-- >>> import Data.Time
53-- >>> import Data.Int
53-- >>> import Data.Int
54-- >>> import Data.Text (Text)
54-- >>> import Data.Text (Text)
55+-- >>> import Data.Time (Day)
55-- >>> import Data.Version
56-- >>> import Data.Version
56
57
57-- $examples
58-- $examples
src/Web/Internal/FormUrlEncoded.hs+32−61
@@ -1,5 +1,5 @@
1−{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DefaultSignatures #-}
4{-# LANGUAGE DefaultSignatures #-}
5{-# LANGUAGE DeriveGeneric #-}
5{-# LANGUAGE DeriveGeneric #-}
@@ -17,50 +17,51 @@
17#include "overlapping-compat.h"
17#include "overlapping-compat.h"
18module Web.Internal.FormUrlEncoded where
18module Web.Internal.FormUrlEncoded where
19
19
20−import Prelude ()
20+#if __GLASGOW_HASKELL__ < 710
21−import Prelude.Compat
21+import Control.Applicative
22+import Data.Traversable
23+#endif
24+
22
25
23−import Control.Applicative (Const(Const))
24import Control.Arrow ((***))
26import Control.Arrow ((***))
25import Control.Monad ((<=<))
27import Control.Monad ((<=<))
26import Data.ByteString.Builder (shortByteString, toLazyByteString)
28import Data.ByteString.Builder (shortByteString, toLazyByteString)
27import qualified Data.ByteString.Lazy as BSL
29import qualified Data.ByteString.Lazy as BSL
28import qualified Data.ByteString.Lazy.Char8 as BSL8
30import qualified Data.ByteString.Lazy.Char8 as BSL8
29−import Data.Coerce (coerce)
30import qualified Data.Foldable as F
31import qualified Data.Foldable as F
31−import Data.Functor.Identity (Identity(Identity))
32import Data.Hashable (Hashable)
32import Data.Hashable (Hashable)
33import Data.HashMap.Strict (HashMap)
33import Data.HashMap.Strict (HashMap)
34import qualified Data.HashMap.Strict as HashMap
34import qualified Data.HashMap.Strict as HashMap
35−import Data.Int (Int16, Int32, Int64, Int8)
35+import Data.Int
36import Data.IntMap (IntMap)
36import Data.IntMap (IntMap)
37import qualified Data.IntMap as IntMap
37import qualified Data.IntMap as IntMap
38import Data.List (intersperse, sortBy)
38import Data.List (intersperse, sortBy)
39import Data.Map (Map)
39import Data.Map (Map)
40import qualified Data.Map as Map
40import qualified Data.Map as Map
41−import Data.Monoid (All (..), Any (..), Dual (..),
41+import Data.Monoid
42− Product (..), Sum (..))
43import Data.Ord (comparing)
42import Data.Ord (comparing)
44−import Data.Proxy (Proxy (..))
45−import Data.Semigroup (Semigroup (..))
46import qualified Data.Semigroup as Semi
43import qualified Data.Semigroup as Semi
47−import Data.Tagged (Tagged (..))
44+
48import Data.Text (Text)
45import Data.Text (Text)
49import qualified Data.Text as Text
46import qualified Data.Text as Text
50−import qualified Data.Text.Encoding as Text
47+import Data.Text.Encoding as Text
51import Data.Text.Encoding.Error (lenientDecode)
48import Data.Text.Encoding.Error (lenientDecode)
52import qualified Data.Text.Lazy as Lazy
49import qualified Data.Text.Lazy as Lazy
53−import Data.Time.Compat (Day, LocalTime, NominalDiffTime,
50+
54− UTCTime, ZonedTime)
51+import Data.Proxy
55−import Data.Time.Calendar.Month.Compat (Month)
56−import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..))
52+import Data.Time
57−import Data.Void (Void)
58−import Data.Word (Word16, Word32, Word64, Word8)
53+import Data.Word
54+
55+#if MIN_VERSION_base(4,8,0)
56+import Data.Void
57+import Numeric.Natural
58+#endif
59+
59import GHC.Exts (Constraint, IsList (..))
60import GHC.Exts (Constraint, IsList (..))
60import GHC.Generics
61import GHC.Generics
61import GHC.TypeLits
62import GHC.TypeLits
62import Network.HTTP.Types.URI (urlDecode, urlEncodeBuilder)
63import Network.HTTP.Types.URI (urlDecode, urlEncodeBuilder)
63−import Numeric.Natural (Natural)
64+
64import Web.Internal.HttpApiData
65import Web.Internal.HttpApiData
65
66
66-- $setup
67-- $setup
@@ -115,9 +116,6 @@ instance ToFormKey LocalTime where toFormKey = toQueryParam
115instance ToFormKey ZonedTime where toFormKey = toQueryParam
116instance ToFormKey ZonedTime where toFormKey = toQueryParam
116instance ToFormKey UTCTime where toFormKey = toQueryParam
117instance ToFormKey UTCTime where toFormKey = toQueryParam
117instance ToFormKey NominalDiffTime where toFormKey = toQueryParam
118instance ToFormKey NominalDiffTime where toFormKey = toQueryParam
118−instance ToFormKey Quarter where toFormKey = toQueryParam
119−instance ToFormKey QuarterOfYear where toFormKey = toQueryParam
120−instance ToFormKey Month where toFormKey = toQueryParam
121
119
122instance ToFormKey String where toFormKey = toQueryParam
120instance ToFormKey String where toFormKey = toQueryParam
123instance ToFormKey Text where toFormKey = toQueryParam
121instance ToFormKey Text where toFormKey = toQueryParam
@@ -126,26 +124,14 @@ instance ToFormKey Lazy.Text where toFormKey = toQueryParam
126instance ToFormKey All where toFormKey = toQueryParam
124instance ToFormKey All where toFormKey = toQueryParam
127instance ToFormKey Any where toFormKey = toQueryParam
125instance ToFormKey Any where toFormKey = toQueryParam
128
126
129−instance ToFormKey a => ToFormKey (Dual a) where toFormKey = coerce (toFormKey :: a -> Text)
127+instance ToFormKey a => ToFormKey (Dual a) where toFormKey = toFormKey . getDual
130−instance ToFormKey a => ToFormKey (Sum a) where toFormKey = coerce (toFormKey :: a -> Text)
128+instance ToFormKey a => ToFormKey (Sum a) where toFormKey = toFormKey . getSum
131−instance ToFormKey a => ToFormKey (Product a) where toFormKey = coerce (toFormKey :: a -> Text)
129+instance ToFormKey a => ToFormKey (Product a) where toFormKey = toFormKey . getProduct
132−
133−instance ToFormKey a => ToFormKey (Semi.Min a) where toFormKey = coerce (toFormKey :: a -> Text)
134−instance ToFormKey a => ToFormKey (Semi.Max a) where toFormKey = coerce (toFormKey :: a -> Text)
135−instance ToFormKey a => ToFormKey (Semi.First a) where toFormKey = coerce (toFormKey :: a -> Text)
136−instance ToFormKey a => ToFormKey (Semi.Last a) where toFormKey = coerce (toFormKey :: a -> Text)
137−
138−instance ToFormKey a => ToFormKey (Tagged b a) where toFormKey = coerce (toFormKey :: a -> Text)
139−
140−-- | @since 0.4.2
141−instance ToFormKey a => ToFormKey (Identity a) where toFormKey = coerce (toFormKey :: a -> Text)
142−
143−-- | @since 0.4.2
144−instance ToFormKey a => ToFormKey (Const a b) where
145− toFormKey = coerce (toFormKey :: a -> Text)
146
130
131+#if MIN_VERSION_base(4,8,0)
147instance ToFormKey Void where toFormKey = toQueryParam
132instance ToFormKey Void where toFormKey = toQueryParam
148instance ToFormKey Natural where toFormKey = toQueryParam
133instance ToFormKey Natural where toFormKey = toQueryParam
134+#endif
149
135
150-- | Typeclass for types that can be parsed from keys of a 'Form'. This is the reverse of 'ToFormKey'.
136-- | Typeclass for types that can be parsed from keys of a 'Form'. This is the reverse of 'ToFormKey'.
151class FromFormKey k where
137class FromFormKey k where
@@ -177,9 +163,6 @@ instance FromFormKey LocalTime where parseFormKey = parseQueryParam
177instance FromFormKey ZonedTime where parseFormKey = parseQueryParam
163instance FromFormKey ZonedTime where parseFormKey = parseQueryParam
178instance FromFormKey UTCTime where parseFormKey = parseQueryParam
164instance FromFormKey UTCTime where parseFormKey = parseQueryParam
179instance FromFormKey NominalDiffTime where parseFormKey = parseQueryParam
165instance FromFormKey NominalDiffTime where parseFormKey = parseQueryParam
180−instance FromFormKey Quarter where parseFormKey = parseQueryParam
181−instance FromFormKey QuarterOfYear where parseFormKey = parseQueryParam
182−instance FromFormKey Month where parseFormKey = parseQueryParam
183
166
184instance FromFormKey String where parseFormKey = parseQueryParam
167instance FromFormKey String where parseFormKey = parseQueryParam
185instance FromFormKey Text where parseFormKey = parseQueryParam
168instance FromFormKey Text where parseFormKey = parseQueryParam
@@ -188,32 +171,20 @@ instance FromFormKey Lazy.Text where parseFormKey = parseQueryParam
188instance FromFormKey All where parseFormKey = parseQueryParam
171instance FromFormKey All where parseFormKey = parseQueryParam
189instance FromFormKey Any where parseFormKey = parseQueryParam
172instance FromFormKey Any where parseFormKey = parseQueryParam
190
173
191−instance FromFormKey a => FromFormKey (Dual a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
174+instance FromFormKey a => FromFormKey (Dual a) where parseFormKey = fmap Dual . parseFormKey
192−instance FromFormKey a => FromFormKey (Sum a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
175+instance FromFormKey a => FromFormKey (Sum a) where parseFormKey = fmap Sum . parseFormKey
193−instance FromFormKey a => FromFormKey (Product a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
194−
195−instance FromFormKey a => FromFormKey (Semi.Min a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
196−instance FromFormKey a => FromFormKey (Semi.Max a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
197−instance FromFormKey a => FromFormKey (Semi.First a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
198−instance FromFormKey a => FromFormKey (Semi.Last a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
199−
200−instance FromFormKey a => FromFormKey (Tagged b a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
176+instance FromFormKey a => FromFormKey (Product a) where parseFormKey = fmap Product . parseFormKey
201−
202−-- | @since 0.4.2
203−instance FromFormKey a => FromFormKey (Identity a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
204−
205−-- | @since 0.4.2
206−instance FromFormKey a => FromFormKey (Const a b) where
207− parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
208
177
178+#if MIN_VERSION_base(4,8,0)
209instance FromFormKey Void where parseFormKey = parseQueryParam
179instance FromFormKey Void where parseFormKey = parseQueryParam
210instance FromFormKey Natural where parseFormKey = parseQueryParam
180instance FromFormKey Natural where parseFormKey = parseQueryParam
181+#endif
211
182
212-- | The contents of a form, not yet URL-encoded.
183-- | The contents of a form, not yet URL-encoded.
213--
184--
214-- 'Form' can be URL-encoded with 'urlEncodeForm' and URL-decoded with 'urlDecodeForm'.
185-- 'Form' can be URL-encoded with 'urlEncodeForm' and URL-decoded with 'urlDecodeForm'.
215newtype Form = Form { unForm :: HashMap Text [Text] }
186newtype Form = Form { unForm :: HashMap Text [Text] }
216− deriving (Eq, Read, Generic, Semigroup, Monoid)
187+ deriving (Eq, Read, Generic, Semi.Semigroup, Monoid)
217
188
218instance Show Form where
189instance Show Form where
219 showsPrec d form = showParen (d > 10) $
190 showsPrec d form = showParen (d > 10) $
src/Web/Internal/HttpApiData.hs+72−242
@@ -5,9 +5,7 @@
5{-# LANGUAGE DeriveFunctor #-}
5{-# LANGUAGE DeriveFunctor #-}
6{-# LANGUAGE DeriveTraversable #-}
6{-# LANGUAGE DeriveTraversable #-}
7{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE KindSignatures #-}
9{-# LANGUAGE OverloadedStrings #-}
8{-# LANGUAGE OverloadedStrings #-}
10−{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE TypeSynonymInstances #-}
10{-# LANGUAGE TypeSynonymInstances #-}
13-- |
11-- |
@@ -15,72 +13,65 @@
15-- such as URL pieces, headers and query parameters.
13-- such as URL pieces, headers and query parameters.
16module Web.Internal.HttpApiData where
14module Web.Internal.HttpApiData where
17
15
18−import Prelude ()
16+#if __GLASGOW_HASKELL__ < 710
19−import Prelude.Compat
17+import Control.Applicative
18+import Data.Foldable (Foldable)
19+import Data.Traversable (Traversable (traverse))
20+#endif
20
21
21−import Control.Applicative (Const(Const))
22import Control.Arrow (left, (&&&))
22import Control.Arrow (left, (&&&))
23import Control.Monad ((<=<))
23import Control.Monad ((<=<))
24−import qualified Data.Attoparsec.Text as Atto
24+
25−import qualified Data.Attoparsec.Time as Atto
26import Data.ByteString (ByteString)
25import Data.ByteString (ByteString)
27import qualified Data.ByteString as BS
26import qualified Data.ByteString as BS
28−import qualified Data.ByteString.Builder as BS
27+import Data.Monoid
29−import qualified Data.ByteString.Lazy as LBS
28+
30−import Data.Coerce (coerce)
31−import Data.Data (Data)
32import qualified Data.Fixed as F
29import qualified Data.Fixed as F
33−import Data.Functor.Identity (Identity(Identity))
30+import Data.Int
34−import Data.Int (Int16, Int32, Int64, Int8)
35−import Data.Kind (Type)
36−import qualified Data.Map as Map
37−import Data.Monoid (All (..), Any (..), Dual (..),
31+import Data.Word
38− First (..), Last (..),
32+
39− Product (..), Sum (..))
40−import Data.Semigroup (Semigroup (..))
41−import qualified Data.Semigroup as Semi
42−import Data.Tagged (Tagged (..))
43import Data.Text (Text)
33import Data.Text (Text)
44import qualified Data.Text as T
34import qualified Data.Text as T
45−import Data.Text.Encoding (decodeUtf8', decodeUtf8With,
35+import Data.Text.Encoding (decodeUtf8', encodeUtf8)
46− encodeUtf8)
47−import Data.Text.Encoding.Error (lenientDecode)
48import qualified Data.Text.Lazy as L
36import qualified Data.Text.Lazy as L
49import Data.Text.Read (Reader, decimal, rational,
37import Data.Text.Read (Reader, decimal, rational,
50 signed)
38 signed)
39+
51−import Data.Time.Compat (Day, FormatTime, LocalTime,
40+import Data.Time
52− NominalDiffTime, TimeOfDay,
41+#if __GLASGOW_HASKELL__ < 710
53− UTCTime, ZonedTime, formatTime,
54− DayOfWeek (..),
55− nominalDiffTimeToSeconds,
56− secondsToNominalDiffTime)
57−import Data.Time.Format.Compat (defaultTimeLocale,
42+import Data.Time.Locale.Compat
58− iso8601DateFormat)
43+#endif
59−import Data.Time.Calendar.Month.Compat (Month)
44+import Data.Version
60−import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..),
45+
61− toYearQuarter)
46+#if MIN_VERSION_base(4,8,0)
62−import Data.Typeable (Typeable)
63−import qualified Data.UUID.Types as UUID
64−import Data.Version (Version, parseVersion,
65− showVersion)
66−import Data.Void (Void, absurd)
47+import Data.Void
67−import Data.Word (Word16, Word32, Word64, Word8)
68−import qualified Network.HTTP.Types as H
69−import Numeric.Natural (Natural)
48+import Numeric.Natural
49+#endif
50+
70import Text.ParserCombinators.ReadP (readP_to_S)
51import Text.ParserCombinators.ReadP (readP_to_S)
71import Text.Read (readMaybe)
52import Text.Read (readMaybe)
72−import Web.Cookie (SetCookie, parseSetCookie,
73− renderSetCookie)
74
53
75#if USE_TEXT_SHOW
54#if USE_TEXT_SHOW
76import TextShow (TextShow, showt)
55import TextShow (TextShow, showt)
77#endif
56#endif
78
57
58+import qualified Data.UUID.Types as UUID
59+
60+import qualified Data.ByteString.Builder as BS
61+import Data.Data (Data)
62+import Data.Typeable (Typeable)
63+import qualified Network.HTTP.Types as H
64+
65+import qualified Data.Attoparsec.Text as Atto
66+import qualified Data.Attoparsec.Time as Atto
67+
68+import Web.Cookie (SetCookie, parseSetCookie,
69+ renderSetCookie)
70+
71+
79-- $setup
72-- $setup
80-- >>> data BasicAuthToken = BasicAuthToken Text deriving (Show)
73-- >>> data BasicAuthToken = BasicAuthToken Text deriving (Show)
81-- >>> instance FromHttpApiData BasicAuthToken where parseHeader h = BasicAuthToken <$> parseHeaderWithPrefix "Basic " h; parseQueryParam p = BasicAuthToken <$> parseQueryParam p
74-- >>> instance FromHttpApiData BasicAuthToken where parseHeader h = BasicAuthToken <$> parseHeaderWithPrefix "Basic " h; parseQueryParam p = BasicAuthToken <$> parseQueryParam p
82−-- >>> import Data.Time.Compat
83−-- >>> import Data.Version
84
75
85-- | Convert value to HTTP API data.
76-- | Convert value to HTTP API data.
86--
77--
@@ -96,7 +87,7 @@ class ToHttpApiData a where
96 -- The default definition uses 'H.encodePathSegmentsRelative',
87 -- The default definition uses 'H.encodePathSegmentsRelative',
97 -- but this may be overriden with a more efficient version.
88 -- but this may be overriden with a more efficient version.
98 toEncodedUrlPiece :: a -> BS.Builder
89 toEncodedUrlPiece :: a -> BS.Builder
99− toEncodedUrlPiece = H.urlEncodeBuilder True . encodeUtf8 . toUrlPiece
90+ toEncodedUrlPiece = H.encodePathSegmentsRelative . (:[]) . toUrlPiece
100
91
101 -- | Convert to HTTP header value.
92 -- | Convert to HTTP header value.
102 toHeader :: a -> ByteString
93 toHeader :: a -> ByteString
@@ -338,7 +329,7 @@ lookupBoundedEnumOf f = flip lookup (map (f &&& id) [minBound..maxBound])
338-- >>> parseBoundedEnumOf toUrlPiece "true" :: Either Text Bool
329-- >>> parseBoundedEnumOf toUrlPiece "true" :: Either Text Bool
339-- Right True
330-- Right True
340--
331--
341−-- For case insensitive parser see 'parseBoundedEnumOfI'.
332+-- For case sensitive parser see 'parseBoundedEnumOfI'.
342parseBoundedEnumOf :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a
333parseBoundedEnumOf :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a
343parseBoundedEnumOf = parseMaybeTextData . lookupBoundedEnumOf
334parseBoundedEnumOf = parseMaybeTextData . lookupBoundedEnumOf
344
335
@@ -439,8 +430,10 @@ instance ToHttpApiData Version where
439 toUrlPiece = T.pack . showVersion
430 toUrlPiece = T.pack . showVersion
440 toEncodedUrlPiece = unsafeToEncodedUrlPiece
431 toEncodedUrlPiece = unsafeToEncodedUrlPiece
441
432
433+#if MIN_VERSION_base(4,8,0)
442instance ToHttpApiData Void where toUrlPiece = absurd
434instance ToHttpApiData Void where toUrlPiece = absurd
443instance ToHttpApiData Natural where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece
435instance ToHttpApiData Natural where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece
436+#endif
444
437
445instance ToHttpApiData Bool where toUrlPiece = showTextData; toEncodedUrlPiece = unsafeToEncodedUrlPiece
438instance ToHttpApiData Bool where toUrlPiece = showTextData; toEncodedUrlPiece = unsafeToEncodedUrlPiece
446instance ToHttpApiData Ordering where toUrlPiece = showTextData; toEncodedUrlPiece = unsafeToEncodedUrlPiece
439instance ToHttpApiData Ordering where toUrlPiece = showTextData; toEncodedUrlPiece = unsafeToEncodedUrlPiece
@@ -459,8 +452,7 @@ instance ToHttpApiData Word16 where toUrlPiece = showt; toEncodedUrlPiece = un
459instance ToHttpApiData Word32 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece
452instance ToHttpApiData Word32 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece
460instance ToHttpApiData Word64 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece
453instance ToHttpApiData Word64 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece
461
454
462−-- | Note: this instance is not polykinded
463−instance F.HasResolution a => ToHttpApiData (F.Fixed (a :: Type)) where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece
455+instance F.HasResolution a => ToHttpApiData (F.Fixed a) where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece
464
456
465-- |
457-- |
466-- >>> toUrlPiece (fromGregorian 2015 10 03)
458-- >>> toUrlPiece (fromGregorian 2015 10 03)
@@ -500,108 +492,36 @@ instance ToHttpApiData UTCTime where
500 toUrlPiece = timeToUrlPiece "%H:%M:%S%QZ"
492 toUrlPiece = timeToUrlPiece "%H:%M:%S%QZ"
501 toEncodedUrlPiece = unsafeToEncodedUrlPiece
493 toEncodedUrlPiece = unsafeToEncodedUrlPiece
502
494
503−-- |
504−-- >>> toUrlPiece Monday
505−-- "monday"
506−instance ToHttpApiData DayOfWeek where
507− toUrlPiece Monday = "monday"
508− toUrlPiece Tuesday = "tuesday"
509− toUrlPiece Wednesday = "wednesday"
510− toUrlPiece Thursday = "thursday"
511− toUrlPiece Friday = "friday"
512− toUrlPiece Saturday = "saturday"
513− toUrlPiece Sunday = "sunday"
514−
515− toEncodedUrlPiece = unsafeToEncodedUrlPiece
516−
517−-- |
518−-- >>> toUrlPiece Q4
519−-- "q4"
520−instance ToHttpApiData QuarterOfYear where
521− toUrlPiece Q1 = "q1"
522− toUrlPiece Q2 = "q2"
523− toUrlPiece Q3 = "q3"
524− toUrlPiece Q4 = "q4"
525−
526−-- |
527−-- >>> import Data.Time.Calendar.Quarter.Compat (Quarter (..))
528−-- >>> MkQuarter 8040
529−-- 2010-Q1
530−--
531−-- >>> toUrlPiece $ MkQuarter 8040
532−-- "2010-q1"
533−--
534−instance ToHttpApiData Quarter where
535− toUrlPiece q = case toYearQuarter q of
536− (y, qoy) -> T.pack (show y ++ "-" ++ f qoy)
537− where
538− f Q1 = "q1"
539− f Q2 = "q2"
540− f Q3 = "q3"
541− f Q4 = "q4"
542−
543−-- |
544−-- >>> import Data.Time.Calendar.Month.Compat (Month (..))
545−-- >>> MkMonth 24482
546−-- 2040-03
547−--
548−-- >>> toUrlPiece $ MkMonth 24482
549−-- "2040-03"
550−--
551−instance ToHttpApiData Month where
552− toUrlPiece = T.pack . formatTime defaultTimeLocale "%Y-%m"
553−
554instance ToHttpApiData NominalDiffTime where
495instance ToHttpApiData NominalDiffTime where
555− toUrlPiece = toUrlPiece . nominalDiffTimeToSeconds
496+ toUrlPiece = toUrlPiece . (floor :: NominalDiffTime -> Integer)
556 toEncodedUrlPiece = unsafeToEncodedUrlPiece
497 toEncodedUrlPiece = unsafeToEncodedUrlPiece
557
498
558instance ToHttpApiData String where toUrlPiece = T.pack
499instance ToHttpApiData String where toUrlPiece = T.pack
559instance ToHttpApiData Text where toUrlPiece = id
500instance ToHttpApiData Text where toUrlPiece = id
560instance ToHttpApiData L.Text where toUrlPiece = L.toStrict
501instance ToHttpApiData L.Text where toUrlPiece = L.toStrict
561
502
562−instance ToHttpApiData All where
503+instance ToHttpApiData All where toUrlPiece = toUrlPiece . getAll; toEncodedUrlPiece = toEncodedUrlPiece . getAll
563− toUrlPiece = coerce (toUrlPiece :: Bool -> Text)
564− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Bool -> BS.Builder)
565−
566−instance ToHttpApiData Any where
504+instance ToHttpApiData Any where toUrlPiece = toUrlPiece . getAny; toEncodedUrlPiece = toEncodedUrlPiece . getAny
567− toUrlPiece = coerce (toUrlPiece :: Bool -> Text)
568− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Bool -> BS.Builder)
569
505
570instance ToHttpApiData a => ToHttpApiData (Dual a) where
506instance ToHttpApiData a => ToHttpApiData (Dual a) where
571− toUrlPiece = coerce (toUrlPiece :: a -> Text)
507+ toUrlPiece = toUrlPiece . getDual
572− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
508+ toEncodedUrlPiece = toEncodedUrlPiece . getDual
573
509
574instance ToHttpApiData a => ToHttpApiData (Sum a) where
510instance ToHttpApiData a => ToHttpApiData (Sum a) where
575− toUrlPiece = coerce (toUrlPiece :: a -> Text)
511+ toUrlPiece = toUrlPiece . getSum
576− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
512+ toEncodedUrlPiece = toEncodedUrlPiece . getSum
577
513
578instance ToHttpApiData a => ToHttpApiData (Product a) where
514instance ToHttpApiData a => ToHttpApiData (Product a) where
579− toUrlPiece = coerce (toUrlPiece :: a -> Text)
515+ toUrlPiece = toUrlPiece . getProduct
580− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
516+ toEncodedUrlPiece = toEncodedUrlPiece . getProduct
581
517
582instance ToHttpApiData a => ToHttpApiData (First a) where
518instance ToHttpApiData a => ToHttpApiData (First a) where
583− toUrlPiece = coerce (toUrlPiece :: Maybe a -> Text)
519+ toUrlPiece = toUrlPiece . getFirst
584− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Maybe a -> BS.Builder)
520+ toEncodedUrlPiece = toEncodedUrlPiece . getFirst
585
521
586instance ToHttpApiData a => ToHttpApiData (Last a) where
522instance ToHttpApiData a => ToHttpApiData (Last a) where
587− toUrlPiece = coerce (toUrlPiece :: Maybe a -> Text)
523+ toUrlPiece = toUrlPiece . getLast
588− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Maybe a -> BS.Builder)
589−
590−instance ToHttpApiData a => ToHttpApiData (Semi.Min a) where
591− toUrlPiece = coerce (toUrlPiece :: a -> Text)
592− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
593−
594−instance ToHttpApiData a => ToHttpApiData (Semi.Max a) where
595− toUrlPiece = coerce (toUrlPiece :: a -> Text)
596− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
524+ toEncodedUrlPiece = toEncodedUrlPiece . getLast
597−
598−instance ToHttpApiData a => ToHttpApiData (Semi.First a) where
599− toUrlPiece = coerce (toUrlPiece :: a -> Text)
600− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
601−
602−instance ToHttpApiData a => ToHttpApiData (Semi.Last a) where
603− toUrlPiece = coerce (toUrlPiece :: a -> Text)
604− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
605
525
606-- |
526-- |
607-- >>> toUrlPiece (Just "Hello")
527-- >>> toUrlPiece (Just "Hello")
@@ -619,40 +539,13 @@ instance (ToHttpApiData a, ToHttpApiData b) => ToHttpApiData (Either a b) where
619 toUrlPiece (Left x) = "left " <> toUrlPiece x
539 toUrlPiece (Left x) = "left " <> toUrlPiece x
620 toUrlPiece (Right x) = "right " <> toUrlPiece x
540 toUrlPiece (Right x) = "right " <> toUrlPiece x
621
541
622−-- | /Note:/ this instance works correctly for alphanumeric name and value
542+-- |
623−--
624-- >>> let Right c = parseUrlPiece "SESSID=r2t5uvjq435r4q7ib3vtdjq120" :: Either Text SetCookie
543-- >>> let Right c = parseUrlPiece "SESSID=r2t5uvjq435r4q7ib3vtdjq120" :: Either Text SetCookie
625-- >>> toUrlPiece c
544-- >>> toUrlPiece c
626−-- "SESSID=r2t5uvjq435r4q7ib3vtdjq120"
545+-- "\"SESSID=r2t5uvjq435r4q7ib3vtdjq120\""
627−--
628−-- >>> toHeader c
629−-- "SESSID=r2t5uvjq435r4q7ib3vtdjq120"
630−--
631instance ToHttpApiData SetCookie where
546instance ToHttpApiData SetCookie where
632− toUrlPiece = decodeUtf8With lenientDecode . toHeader
633− toHeader = LBS.toStrict . BS.toLazyByteString . renderSetCookie
547+ toUrlPiece = showt . BS.toLazyByteString . renderSetCookie
634− -- toEncodedUrlPiece = renderSetCookie -- doesn't do things.
548+ toEncodedUrlPiece = renderSetCookie
635−
636−-- | Note: this instance is not polykinded
637−instance ToHttpApiData a => ToHttpApiData (Tagged (b :: Type) a) where
638− toUrlPiece = coerce (toUrlPiece :: a -> Text)
639− toHeader = coerce (toHeader :: a -> ByteString)
640− toQueryParam = coerce (toQueryParam :: a -> Text)
641− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
642−
643−-- | @since 0.4.2
644−instance ToHttpApiData a => ToHttpApiData (Const a b) where
645− toUrlPiece = coerce (toUrlPiece :: a -> Text)
646− toHeader = coerce (toHeader :: a -> ByteString)
647− toQueryParam = coerce (toQueryParam :: a -> Text)
648− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
649−
650−-- | @since 0.4.2
651−instance ToHttpApiData a => ToHttpApiData (Identity a) where
652− toUrlPiece = coerce (toUrlPiece :: a -> Text)
653− toHeader = coerce (toHeader :: a -> ByteString)
654− toQueryParam = coerce (toQueryParam :: a -> Text)
655− toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
656
549
657-- |
550-- |
658-- >>> parseUrlPiece "_" :: Either Text ()
551-- >>> parseUrlPiece "_" :: Either Text ()
@@ -665,7 +558,7 @@ instance FromHttpApiData Char where
665 parseUrlPiece s =
558 parseUrlPiece s =
666 case T.uncons s of
559 case T.uncons s of
667 Just (c, s') | T.null s' -> pure c
560 Just (c, s') | T.null s' -> pure c
668− _ -> defaultParseError s
561+ _ -> defaultParseError s
669
562
670-- |
563-- |
671-- >>> showVersion <$> parseUrlPiece "1.2.3"
564-- >>> showVersion <$> parseUrlPiece "1.2.3"
@@ -676,6 +569,7 @@ instance FromHttpApiData Version where
676 ((x, ""):_) -> pure x
569 ((x, ""):_) -> pure x
677 _ -> defaultParseError s
570 _ -> defaultParseError s
678
571
572+#if MIN_VERSION_base(4,8,0)
679-- | Parsing a @'Void'@ value is always an error, considering @'Void'@ as a data type with no constructors.
573-- | Parsing a @'Void'@ value is always an error, considering @'Void'@ as a data type with no constructors.
680instance FromHttpApiData Void where
574instance FromHttpApiData Void where
681 parseUrlPiece _ = Left "Void cannot be parsed!"
575 parseUrlPiece _ = Left "Void cannot be parsed!"
@@ -686,6 +580,7 @@ instance FromHttpApiData Natural where
686 if n < 0
580 if n < 0
687 then Left ("underflow: " <> s <> " (should be a non-negative integer)")
581 then Left ("underflow: " <> s <> " (should be a non-negative integer)")
688 else Right (fromInteger n)
582 else Right (fromInteger n)
583+#endif
689
584
690instance FromHttpApiData Bool where parseUrlPiece = parseBoundedUrlPiece
585instance FromHttpApiData Bool where parseUrlPiece = parseBoundedUrlPiece
691instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedUrlPiece
586instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedUrlPiece
@@ -706,10 +601,6 @@ instance FromHttpApiData String where parseUrlPiece = Right . T.unpack
706instance FromHttpApiData Text where parseUrlPiece = Right
601instance FromHttpApiData Text where parseUrlPiece = Right
707instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
602instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
708
603
709−-- | Note: this instance is not polykinded
710−instance F.HasResolution a => FromHttpApiData (F.Fixed (a :: Type)) where
711− parseUrlPiece = runReader rational
712−
713-- |
604-- |
714-- >>> toGregorian <$> parseUrlPiece "2016-12-01"
605-- >>> toGregorian <$> parseUrlPiece "2016-12-01"
715-- Right (2016,12,1)
606-- Right (2016,12,1)
@@ -738,57 +629,16 @@ instance FromHttpApiData ZonedTime where parseUrlPiece = runAtto Atto.zonedTime
738-- Right 2015-10-03 00:14:24 UTC
629-- Right 2015-10-03 00:14:24 UTC
739instance FromHttpApiData UTCTime where parseUrlPiece = runAtto Atto.utcTime
630instance FromHttpApiData UTCTime where parseUrlPiece = runAtto Atto.utcTime
740
631
741−-- |
742−-- >>> parseUrlPiece "Monday" :: Either Text DayOfWeek
743−-- Right Monday
744−instance FromHttpApiData DayOfWeek where
745− parseUrlPiece t = case Map.lookup (T.toLower t) m of
746− Just dow -> Right dow
747− Nothing -> Left $ "Incorrect DayOfWeek: " <> T.take 10 t
748− where
749− m :: Map.Map Text DayOfWeek
750− m = Map.fromList [ (toUrlPiece dow, dow) | dow <- [Monday .. Sunday] ]
751−
752−
753−instance FromHttpApiData NominalDiffTime where parseUrlPiece = fmap secondsToNominalDiffTime . parseUrlPiece
632+instance FromHttpApiData NominalDiffTime where parseUrlPiece = fmap fromInteger . parseUrlPiece
754−
755−-- |
756−-- >>> parseUrlPiece "2021-01" :: Either Text Month
757−-- Right 2021-01
758−instance FromHttpApiData Month where parseUrlPiece = runAtto Atto.month
759
633
760−-- |
634+instance FromHttpApiData All where parseUrlPiece = fmap All . parseUrlPiece
761−-- >>> parseUrlPiece "2021-q1" :: Either Text Quarter
762−-- Right 2021-Q1
763−instance FromHttpApiData Quarter where parseUrlPiece = runAtto Atto.quarter
635+instance FromHttpApiData Any where parseUrlPiece = fmap Any . parseUrlPiece
764
636
765−-- |
766−-- >>> parseUrlPiece "q2" :: Either Text QuarterOfYear
767−-- Right Q2
768−--
769−-- >>> parseUrlPiece "Q3" :: Either Text QuarterOfYear
770−-- Right Q3
771−instance FromHttpApiData QuarterOfYear where
772− parseUrlPiece t = case T.toLower t of
773− "q1" -> return Q1
774− "q2" -> return Q2
775− "q3" -> return Q3
776− "q4" -> return Q4
777− _ -> Left "Invalid quarter of year"
778−
779−instance FromHttpApiData All where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text Bool)
780−instance FromHttpApiData Any where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text Bool)
781−
782−instance FromHttpApiData a => FromHttpApiData (Dual a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
637+instance FromHttpApiData a => FromHttpApiData (Dual a) where parseUrlPiece = fmap Dual . parseUrlPiece
783−instance FromHttpApiData a => FromHttpApiData (Sum a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
638+instance FromHttpApiData a => FromHttpApiData (Sum a) where parseUrlPiece = fmap Sum . parseUrlPiece
784−instance FromHttpApiData a => FromHttpApiData (Product a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
639+instance FromHttpApiData a => FromHttpApiData (Product a) where parseUrlPiece = fmap Product . parseUrlPiece
785−instance FromHttpApiData a => FromHttpApiData (First a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text (Maybe a))
640+instance FromHttpApiData a => FromHttpApiData (First a) where parseUrlPiece = fmap First . parseUrlPiece
786−instance FromHttpApiData a => FromHttpApiData (Last a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text (Maybe a))
787−
788−instance FromHttpApiData a => FromHttpApiData (Semi.Min a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
789−instance FromHttpApiData a => FromHttpApiData (Semi.Max a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
641+instance FromHttpApiData a => FromHttpApiData (Last a) where parseUrlPiece = fmap Last . parseUrlPiece
790−instance FromHttpApiData a => FromHttpApiData (Semi.First a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
791−instance FromHttpApiData a => FromHttpApiData (Semi.Last a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
792
642
793-- |
643-- |
794-- >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
644-- >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
@@ -831,32 +681,12 @@ instance FromHttpApiData a => FromHttpApiData (LenientData a) where
831 parseHeader = Right . LenientData . parseHeader
681 parseHeader = Right . LenientData . parseHeader
832 parseQueryParam = Right . LenientData . parseQueryParam
682 parseQueryParam = Right . LenientData . parseQueryParam
833
683
834−-- | /Note:/ this instance works correctly for alphanumeric name and value
684+-- |
835−--
836-- >>> parseUrlPiece "SESSID=r2t5uvjq435r4q7ib3vtdjq120" :: Either Text SetCookie
685-- >>> parseUrlPiece "SESSID=r2t5uvjq435r4q7ib3vtdjq120" :: Either Text SetCookie
837-- Right (SetCookie {setCookieName = "SESSID", setCookieValue = "r2t5uvjq435r4q7ib3vtdjq120", setCookiePath = Nothing, setCookieExpires = Nothing, setCookieMaxAge = Nothing, setCookieDomain = Nothing, setCookieHttpOnly = False, setCookieSecure = False, setCookieSameSite = Nothing})
686-- Right (SetCookie {setCookieName = "SESSID", setCookieValue = "r2t5uvjq435r4q7ib3vtdjq120", setCookiePath = Nothing, setCookieExpires = Nothing, setCookieMaxAge = Nothing, setCookieDomain = Nothing, setCookieHttpOnly = False, setCookieSecure = False, setCookieSameSite = Nothing})
838instance FromHttpApiData SetCookie where
687instance FromHttpApiData SetCookie where
839 parseUrlPiece = parseHeader . encodeUtf8
688 parseUrlPiece = parseHeader . encodeUtf8
840 parseHeader = Right . parseSetCookie
689 parseHeader = Right . parseSetCookie
841−
842−-- | Note: this instance is not polykinded
843−instance FromHttpApiData a => FromHttpApiData (Tagged (b :: Type) a) where
844− parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
845− parseHeader = coerce (parseHeader :: ByteString -> Either Text a)
846− parseQueryParam = coerce (parseQueryParam :: Text -> Either Text a)
847−
848−-- | @since 0.4.2
849−instance FromHttpApiData a => FromHttpApiData (Const a b) where
850− parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
851− parseHeader = coerce (parseHeader :: ByteString -> Either Text a)
852− parseQueryParam = coerce (parseQueryParam :: Text -> Either Text a)
853−
854−-- | @since 0.4.2
855−instance FromHttpApiData a => FromHttpApiData (Identity a) where
856− parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
857− parseHeader = coerce (parseHeader :: ByteString -> Either Text a)
858− parseQueryParam = coerce (parseQueryParam :: Text -> Either Text a)
859−
860-------------------------------------------------------------------------------
690-------------------------------------------------------------------------------
861-- Attoparsec helpers
691-- Attoparsec helpers
862-------------------------------------------------------------------------------
692-------------------------------------------------------------------------------
test/Web/Internal/HttpApiDataSpec.hs+112−60
@@ -1,38 +1,32 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE ScopedTypeVariables #-}
2module Web.Internal.HttpApiDataSpec (spec) where
3module Web.Internal.HttpApiDataSpec (spec) where
3
4
5+import Control.Applicative
6+import qualified Data.Fixed as F
7+import Data.Int
8+import Data.Word
9+import Data.Time
10+import qualified Data.Text as T
11+import qualified Data.Text.Lazy as L
12+import qualified Data.ByteString as BS
13+import Data.ByteString.Builder (toLazyByteString)
14+import Data.Version
15+import qualified Data.UUID.Types as UUID
4
16
5−import Prelude ()
17+import Data.Proxy
6−import Prelude.Compat
7
18
8−import qualified Data.ByteString as BS
19+#if MIN_VERSION_base(4,8,0)
9−import Data.ByteString.Builder (toLazyByteString)
20+import Numeric.Natural
10−import Data.Char
21+#endif
11−import qualified Data.Fixed as F
12−import Data.Int
13−import qualified Data.Text as T
14−import qualified Data.Text.Encoding as TE
15−import qualified Data.Text.Lazy as L
16−import Data.Time.Compat
17−import Data.Time.Calendar.Month.Compat (Month)
18−import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..))
19−import qualified Data.UUID.Types as UUID
20−import Data.Version
21−import Data.Word
22−import Web.Cookie (SetCookie, defaultSetCookie,
23− setCookieName, setCookieValue)
24
22
25−import Data.Proxy
23+import Test.Hspec
24+import Test.Hspec.QuickCheck(prop)
25+import Test.QuickCheck
26
26
27−import Numeric.Natural
27+import Web.Internal.HttpApiData
28
28
29−import Test.Hspec
30−import Test.Hspec.QuickCheck (prop)
31−import Test.QuickCheck
32−
33−import Web.Internal.HttpApiData
34−
35−import Web.Internal.TestInstances
29+import Web.Internal.TestInstances
36
30
37(<=>) :: forall a b. (Show a, Show b, Eq a) => (a -> b) -> (b -> Either T.Text a) -> a -> Property
31(<=>) :: forall a b. (Show a, Show b, Eq a) => (a -> b) -> (b -> Either T.Text a) -> a -> Property
38(f <=> g) x = counterexample
32(f <=> g) x = counterexample
@@ -46,24 +40,30 @@ import Web.Internal.TestInstances
46encodedUrlPieceProp :: ToHttpApiData a => a -> Property
40encodedUrlPieceProp :: ToHttpApiData a => a -> Property
47encodedUrlPieceProp x = toLazyByteString (toEncodedUrlPiece (toUrlPiece x)) === toLazyByteString (toEncodedUrlPiece x)
41encodedUrlPieceProp x = toLazyByteString (toEncodedUrlPiece (toUrlPiece x)) === toLazyByteString (toEncodedUrlPiece x)
48
42
49−-- | Check 'ToHttpApiData' and 'FromHttpApiData' compatibility
43+
50checkUrlPiece :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a, Arbitrary a) => Proxy a -> String -> Spec
44checkUrlPiece :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a, Arbitrary a) => Proxy a -> String -> Spec
51−checkUrlPiece _ = checkUrlPiece' (arbitrary :: Gen a)
45+checkUrlPiece _ name = prop name (toUrlPiece <=> parseUrlPiece :: a -> Property)
52
46
47+-- | Check with given generator
53checkUrlPiece' :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a) => Gen a -> String -> Spec
48checkUrlPiece' :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a) => Gen a -> String -> Spec
54−checkUrlPiece' gen name = describe name $ do
49+checkUrlPiece' gen name = prop name $ forAll gen (toUrlPiece <=> parseUrlPiece)
55− prop "toUrlPiece <=> parseUrlPiece" $ forAll gen (toUrlPiece <=> parseUrlPiece :: a -> Property)
56− prop "toQueryParam <=> parseQueryParam" $ forAll gen (toQueryParam <=> parseQueryParam :: a -> Property)
57− prop "toHeader <=> parseHeader" $ forAll gen (toHeader <=> parseHeader :: a -> Property)
58− prop "toEncodedUrlPiece encodes correctly" $ forAll gen encodedUrlPieceProp
59
50
60-- | Check case insensitivity for @parseUrlPiece@.
51-- | Check case insensitivity for @parseUrlPiece@.
61checkUrlPieceI :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Arbitrary a) => Proxy a -> String -> Spec
52checkUrlPieceI :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Arbitrary a) => Proxy a -> String -> Spec
62checkUrlPieceI _ = checkUrlPiece (Proxy :: Proxy (RandomCase a))
53checkUrlPieceI _ = checkUrlPiece (Proxy :: Proxy (RandomCase a))
63
54
55+-- | Check that 'toEncodedUrlPiece' is equivallent to default implementation.
56+checkEncodedUrlPiece :: forall a. (Show a, ToHttpApiData a, Arbitrary a) => Proxy a -> String -> Spec
57+checkEncodedUrlPiece _ = checkEncodedUrlPiece' (arbitrary :: Gen a)
58+
59+-- | Check that 'toEncodedUrlPiece' is equivallent to default implementation.
60+-- Use a given generator.
61+checkEncodedUrlPiece' :: forall a. (Show a, ToHttpApiData a) => Gen a -> String -> Spec
62+checkEncodedUrlPiece' gen name = prop name $ forAll gen encodedUrlPieceProp
63+
64spec :: Spec
64spec :: Spec
65spec = do
65spec = do
66− describe "Instances" $ do
66+ describe "toUrlPiece <=> parseUrlPiece" $ do
67 checkUrlPiece (Proxy :: Proxy ()) "()"
67 checkUrlPiece (Proxy :: Proxy ()) "()"
68 checkUrlPiece (Proxy :: Proxy Char) "Char"
68 checkUrlPiece (Proxy :: Proxy Char) "Char"
69 checkUrlPieceI (Proxy :: Proxy Bool) "Bool"
69 checkUrlPieceI (Proxy :: Proxy Bool) "Bool"
@@ -83,34 +83,67 @@ spec = do
83 checkUrlPiece (Proxy :: Proxy T.Text) "Text.Strict"
83 checkUrlPiece (Proxy :: Proxy T.Text) "Text.Strict"
84 checkUrlPiece (Proxy :: Proxy L.Text) "Text.Lazy"
84 checkUrlPiece (Proxy :: Proxy L.Text) "Text.Lazy"
85 checkUrlPiece (Proxy :: Proxy Day) "Day"
85 checkUrlPiece (Proxy :: Proxy Day) "Day"
86− checkUrlPiece (Proxy :: Proxy TimeOfDay) "TimeOfDay"
86+ checkUrlPiece' timeOfDayGen "TimeOfDay"
87− checkUrlPiece (Proxy :: Proxy LocalTime) "LocalTime"
87+ checkUrlPiece' localTimeGen "LocalTime"
88− checkUrlPiece (Proxy :: Proxy ZonedTime) "ZonedTime"
88+ checkUrlPiece' zonedTimeGen "ZonedTime"
89− checkUrlPiece (Proxy :: Proxy UTCTime) "UTCTime"
89+ checkUrlPiece' utcTimeGen "UTCTime"
90− checkUrlPiece (Proxy :: Proxy NominalDiffTime) "NominalDiffTime"
90+ checkUrlPiece' nominalDiffTimeGen "NominalDiffTime"
91− checkUrlPiece (Proxy :: Proxy DayOfWeek) "DayOfWeek"
92− checkUrlPiece (Proxy :: Proxy Quarter) "Quarter"
93− checkUrlPiece (Proxy :: Proxy QuarterOfYear) "QuarterOfYear"
94− checkUrlPiece (Proxy :: Proxy Month) "Month"
95− checkUrlPiece (Proxy :: Proxy DayOfWeek) "DayOfWeek"
96 checkUrlPiece (Proxy :: Proxy Version) "Version"
91 checkUrlPiece (Proxy :: Proxy Version) "Version"
97− checkUrlPiece (Proxy :: Proxy UUID.UUID) "UUID"
92+ checkUrlPiece' uuidGen "UUID"
98− checkUrlPiece' setCookieGen "Cookie"
99−
100− checkUrlPiece (Proxy :: Proxy F.Uni) "Uni"
101− checkUrlPiece (Proxy :: Proxy F.Deci) "Deci"
102− checkUrlPiece (Proxy :: Proxy F.Centi) "Centi"
103− checkUrlPiece (Proxy :: Proxy F.Milli) "Milli"
104− checkUrlPiece (Proxy :: Proxy F.Micro) "Micro"
105− checkUrlPiece (Proxy :: Proxy F.Nano) "Nano"
106− checkUrlPiece (Proxy :: Proxy F.Pico) "Pico"
107
93
108 checkUrlPiece (Proxy :: Proxy (Maybe String)) "Maybe String"
94 checkUrlPiece (Proxy :: Proxy (Maybe String)) "Maybe String"
109 checkUrlPieceI (Proxy :: Proxy (Maybe Integer)) "Maybe Integer"
95 checkUrlPieceI (Proxy :: Proxy (Maybe Integer)) "Maybe Integer"
110 checkUrlPiece (Proxy :: Proxy (Either Integer T.Text)) "Either Integer Text"
96 checkUrlPiece (Proxy :: Proxy (Either Integer T.Text)) "Either Integer Text"
111 checkUrlPieceI (Proxy :: Proxy (Either Version Day)) "Either Version Day"
97 checkUrlPieceI (Proxy :: Proxy (Either Version Day)) "Either Version Day"
112
98
99+#if MIN_VERSION_base(4,8,0)
113 checkUrlPiece (Proxy :: Proxy Natural) "Natural"
100 checkUrlPiece (Proxy :: Proxy Natural) "Natural"
101+#endif
102+
103+ describe "toEncodedUrlPiece encodes correctly" $ do
104+ checkEncodedUrlPiece (Proxy :: Proxy ()) "()"
105+ checkEncodedUrlPiece (Proxy :: Proxy Char) "Char"
106+ checkEncodedUrlPiece (Proxy :: Proxy Bool) "Bool"
107+ checkEncodedUrlPiece (Proxy :: Proxy Ordering) "Ordering"
108+ checkEncodedUrlPiece (Proxy :: Proxy Int) "Int"
109+ checkEncodedUrlPiece (Proxy :: Proxy Int8) "Int8"
110+ checkEncodedUrlPiece (Proxy :: Proxy Int16) "Int16"
111+ checkEncodedUrlPiece (Proxy :: Proxy Int32) "Int32"
112+ checkEncodedUrlPiece (Proxy :: Proxy Int64) "Int64"
113+ checkEncodedUrlPiece (Proxy :: Proxy Integer) "Integer"
114+ checkEncodedUrlPiece (Proxy :: Proxy Word) "Word"
115+ checkEncodedUrlPiece (Proxy :: Proxy Word8) "Word8"
116+ checkEncodedUrlPiece (Proxy :: Proxy Word16) "Word16"
117+ checkEncodedUrlPiece (Proxy :: Proxy Word32) "Word32"
118+ checkEncodedUrlPiece (Proxy :: Proxy Word64) "Word64"
119+ checkEncodedUrlPiece (Proxy :: Proxy String) "String"
120+ checkEncodedUrlPiece (Proxy :: Proxy T.Text) "Text.Strict"
121+ checkEncodedUrlPiece (Proxy :: Proxy L.Text) "Text.Lazy"
122+ checkEncodedUrlPiece (Proxy :: Proxy Day) "Day"
123+ checkEncodedUrlPiece' timeOfDayGen "TimeOfDay"
124+ checkEncodedUrlPiece' localTimeGen "LocalTime"
125+ checkEncodedUrlPiece' zonedTimeGen "ZonedTime"
126+ checkEncodedUrlPiece' utcTimeGen "UTCTime"
127+ checkEncodedUrlPiece' nominalDiffTimeGen "NominalDiffTime"
128+ checkEncodedUrlPiece (Proxy :: Proxy Version) "Version"
129+ checkEncodedUrlPiece' uuidGen "UUID"
130+
131+ checkEncodedUrlPiece (Proxy :: Proxy (Maybe String)) "Maybe String"
132+ checkEncodedUrlPiece (Proxy :: Proxy (Maybe Integer)) "Maybe Integer"
133+ checkEncodedUrlPiece (Proxy :: Proxy (Either Integer T.Text)) "Either Integer Text"
134+ checkEncodedUrlPiece (Proxy :: Proxy (Either Version Day)) "Either Version Day"
135+
136+#if MIN_VERSION_base(4,8,0)
137+ checkEncodedUrlPiece (Proxy :: Proxy Natural) "Natural"
138+#endif
139+
140+ checkEncodedUrlPiece (Proxy :: Proxy F.Uni) "Uni"
141+ checkEncodedUrlPiece (Proxy :: Proxy F.Deci) "Deci"
142+ checkEncodedUrlPiece (Proxy :: Proxy F.Centi) "Centi"
143+ checkEncodedUrlPiece (Proxy :: Proxy F.Milli) "Milli"
144+ checkEncodedUrlPiece (Proxy :: Proxy F.Micro) "Micro"
145+ checkEncodedUrlPiece (Proxy :: Proxy F.Nano) "Nano"
146+ checkEncodedUrlPiece (Proxy :: Proxy F.Pico) "Pico"
114
147
115 it "bad integers are rejected" $ do
148 it "bad integers are rejected" $ do
116 parseUrlPieceMaybe (T.pack "123hello") `shouldBe` (Nothing :: Maybe Int)
149 parseUrlPieceMaybe (T.pack "123hello") `shouldBe` (Nothing :: Maybe Int)
@@ -122,8 +155,27 @@ spec = do
122 it "invalid utf8 is handled" $ do
155 it "invalid utf8 is handled" $ do
123 parseHeaderMaybe (BS.pack [128]) `shouldBe` (Nothing :: Maybe T.Text)
156 parseHeaderMaybe (BS.pack [128]) `shouldBe` (Nothing :: Maybe T.Text)
124
157
125−setCookieGen :: Gen SetCookie
158+
126−setCookieGen = do
159+uuidGen :: Gen UUID.UUID
127− n <- TE.encodeUtf8 . T.pack . filter isAlphaNum <$> arbitrary
160+uuidGen = UUID.fromWords <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
128− v <- TE.encodeUtf8 . T.pack . filter isAlphaNum <$> arbitrary
161+
129− return $ defaultSetCookie { setCookieName = n, setCookieValue = v }
162+-- TODO: this generators don't generate full range items
163+localTimeGen :: Gen LocalTime
164+localTimeGen = LocalTime <$> arbitrary <*> timeOfDayGen
165+
166+timeOfDayGen :: Gen TimeOfDay
167+timeOfDayGen = TimeOfDay
168+ <$> choose (0, 23)
169+ <*> choose (0, 59)
170+ <*> fmap (\x -> 0.1 * fromInteger x) (choose (0, 600))
171+
172+zonedTimeGen :: Gen ZonedTime
173+zonedTimeGen = ZonedTime
174+ <$> localTimeGen -- Note: not arbitrary!
175+ <*> liftA3 TimeZone arbitrary arbitrary (vectorOf 3 (elements ['A'..'Z']))
176+
177+utcTimeGen :: Gen UTCTime
178+utcTimeGen = UTCTime <$> arbitrary <*> fmap fromInteger (choose (0, 86400))
179+
180+nominalDiffTimeGen :: Gen NominalDiffTime
181+nominalDiffTimeGen = fromInteger <$> arbitrary
test/Web/Internal/TestInstances.hs+1−1
@@ -12,7 +12,7 @@ import Control.Applicative
12import Data.Char
12import Data.Char
13import qualified Data.HashMap.Strict as HashMap
13import qualified Data.HashMap.Strict as HashMap
14import qualified Data.Text as T
14import qualified Data.Text as T
15−import Data.Time.Compat
15+import Data.Time
16import GHC.Exts (fromList)
16import GHC.Exts (fromList)
17import GHC.Generics
17import GHC.Generics
18
18
test/doctests.hsnew file+25
@@ -0,0 +1,25 @@
1+-----------------------------------------------------------------------------
2+-- |
3+-- Module : Main (doctests)
4+-- Copyright : (C) 2012-14 Edward Kmett
5+-- License : BSD-style (see the file LICENSE)
6+-- Maintainer : Edward Kmett <ekmett@gmail.com>
7+-- Stability : provisional
8+-- Portability : portable
9+--
10+-- This module provides doctests for a project based on the actual versions
11+-- of the packages it was built with. It requires a corresponding Setup.lhs
12+-- to be added to the project
13+-----------------------------------------------------------------------------
14+module Main where
15+
16+import Build_doctests (flags, pkgs, module_sources)
17+import Data.Foldable (traverse_)
18+import Test.DocTest
19+
20+main :: IO ()
21+main = do
22+ traverse_ putStrLn args
23+ doctest args
24+ where
25+ args = flags ++ pkgs ++ module_sources