77801d13

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

Derives `Web.FormUrlEncoded.ToForm` and `FromForm` from a `HasObjectCodec`
instance, so a type with a codec can be posted as
`application/x-www-form-urlencoded` without a second, hand-written description
of its fields.

## Why

The motivating case is an API endpoint that a shell should be able to hit with
curl alone:

```
curl --netrc --silent --show-error --fail-with-body \
  --data-urlencode "clone-url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git" \
  --data-urlencode "message=$COMMIT_MESSAGE" \
  https://api.nix-ci.com/suite
```

The point of the form is that `--data-urlencode` escapes each value, so a commit
message containing a quote or a newline needs no JSON encoder in the shell.

The type already had a `HasObjectCodec` instance naming all its fields, but with
no autodocodec support the `FromForm` instance had to be written by hand, so the
field names were spelled twice and a test had to assert the two lists agreed.
That test is exactly the check this package makes unnecessary.

`http-api-data`'s own generic derivation is not a substitute: it derives names
from Haskell record fields via a label modifier, so it would be a third
independent spelling of the same names rather than a shared one.

## What

`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 `[Input]`.

Three places where copying multipart verbatim would have been wrong:

- **`unionForm`, not `<>`.** `Form` newtype-derives `Semigroup` from `HashMap`,
  whose union is left-biased, so `<>` would silently drop the second form's
  values under a shared key — where multipart's `mappendMultipartData`
  concatenates.
- **Booleans encode lower case.** Multipart's encoder emits Haskell spelling;
  `toUrlPiece @Bool` is `T.toLower . show`, so `"True"` would round-trip through
  our own decoder but read as unconventional to every other form consumer. The
  decoder still accepts either spelling.
- **A field is absent exactly when its key is absent**, which is lossless and
  matches `http-api-data`'s own `lookupMaybe`. `dropEmptyFormValues :: Form ->
  Form` is exported separately for callers who need a shell's unset variable
  (`--data-urlencode "x=$UNSET"` sends `x=`) to read as absence, at the cost of
  making a genuinely empty value inexpressible. That is a caller's choice rather
  than a policy threaded through the interpreter.

Nested values keep multipart's behaviour — JSON-encoded into the text slot —
documented in the module header as something no other form parser will
understand.

## Tests

`autodocodec-api-usage/test/Autodocodec/FormUrlEncodedSpec.hs` mirrors
`MultipartSpec`: "matches the encoding", "matches the decoding" and a
round trip, over `Example`, `Via`, `LegacyValue`, `LegacyObject`, `These`,
`Expression`, `ListsExample` and `Overlap`. Hand-written `ToForm`/`FromForm
Example` in `Usage.hs` give the first two something independent to compare
against, as the multipart pair do.

Two additions beyond that mirror:

- `dropEmptyFormValues` asserted on both sides of the policy: an empty value
  stays a value without it, and reads as an absent field with it.
- A round trip through `urlEncodeFormStable`/`urlDecodeForm`, so the actual
  percent-escaped wire bytes are covered. That escaping is the reason the
  feature exists.

**No `xdescribe` was needed.** `MultipartSpec` skips `Example` with "does not
hold."; here the whole corpus round-trips, `Example` included. I ran 480,000
examples per property rather than trusting the default 100 before concluding
that. Grouping repeated keys is what makes the difference.

## Checks

- `nix flake check` passes. The new package builds under `-Werror` against
  nixpkgs 26.05, 25.11 and 25.05 plus horizon-advance, so against four
  `http-api-data` versions.
- Full `autodocodec-api-usage` suite: 77,482 examples, 0 failures.
- `pre-commit run -a` clean.

`stack build --pedantic` cannot pass in this tree, and not because of this
change: `autodocodec-swagger2` has an unused `aeson` dependency that
`-Wunused-packages` rejects under `-Werror`. I confirmed that on a clean tree
before working around it. It does not affect `nix flake check`, whose override
does not enable that warning, but it does mean the cheapest feedback loop is
unusable until a separate one-line fix to `autodocodec-swagger2/package.yaml`.

## Not in this PR

`cabal.project` needs no change (it globs `*/*.cabal`); `stack.yaml` does, since
it lists packages explicitly. Replacing the first hand-written caller lives in
the nix-ci tree, on branch `openapi-spec` (NorfairKing/nix-ci#463).
forwardCompatibilitynixpkgs-25_05nixpkgs-25_11pre-commitreleaseconfiguredevShellautodocodecautodocodec-api-usageautodocodec-exactautodocodec-http-api-dataautodocodec-nixautodocodec-openapi3autodocodec-schemaautodocodec-servant-multipartautodocodec-swagger2autodocodec-yamlx86_64-linux