7fc86d3d

By: Tom Sydney Kerckhove <syd@cs-syd.eu>

Add autodocodec-http-api-data for form-urlencoded data

A type with a HasObjectCodec instance could not be posted as
application/x-www-form-urlencoded without a second, hand-written
description of its fields. http-api-data's own generic derivation is no
substitute: it derives names from Haskell record fields via a label
modifier, so it would be a third independent spelling of the same field
names rather than a shared one.

This is autodocodec-servant-multipart's interpreter retargeted at
Web.FormUrlEncoded.Form, which is likewise a flat map from text keys to
text values. A Form's HashMap Text [Text] models repeated keys natively,
so ArrayOfCodec fits it better than it fits multipart's flat list of
inputs, and unlike the multipart spec every corpus type round-trips,
Example included.

Three places where copying multipart verbatim would have been wrong:

* Form derives Semigroup from HashMap, whose union is left-biased and so
  would silently drop the second form's values under a shared key, where
  multipart's mappend concatenates. Hence unionForm.

* Booleans encode lower case, to match toUrlPiece @Bool and the HTML form
  convention, so that a value survives http-api-data's own parser. The
  decoder still accepts either spelling.

* A form has no null, so whether "key=" means the empty string or means
  absence is a real choice. It is FormDecodeSettings, consulted by the
  decoder at optional keys only: a required key has no absence for an
  empty value to mean, so it decodes the empty string either way. The
  default is lossless.

  That distinction only exists inside the interpreter, which is why the
  setting lives there. A pre-pass over the Form cannot make it, because it
  would strip a required field's legitimate empty string along with the
  optional field's unset one.

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 7m03s 1s 1s 7m04s 7m03s
Eval 9m12s 1m15s 1m15s 10m28s 2m07s
Build 10m24s 0s 0s 10m24s 0s
Suite 7m03s 1m17s 3m24s 10m28s 9m10s

Timeline

0s9m20s9m30s9m40s9m50s10m10m10s10m20s