composer-php-json 0.2.0

Byte-exact PHP json_encode output for the two flag combinations Composer relies on (content-hash + JsonFile::encode).
Documentation
# composer-php-json

Byte-exact [PHP `json_encode`](https://www.php.net/json_encode) output for the
two flag combinations [Composer](https://getcomposer.org/) relies on — so Rust
tooling can reproduce Composer's `content-hash` and its `composer.json` /
`composer.lock` file formatting byte-for-byte.

```rust
use composer_php_json::{encode, Mode};
use serde_json::json;

// Composer's content-hash encoding: compact, escaped slashes, \uXXXX non-ASCII.
let bytes = encode(&json!({"name": "acme/widget"}), Mode::Hash);
assert_eq!(String::from_utf8(bytes).unwrap(), r#"{"name":"acme\/widget"}"#);
```

## Modes

- **`Mode::Hash`**`json_encode($d, 0)`. The byte stream MD5'd into
  `composer.lock`'s `content-hash`. Compact, `/``\/`, every non-ASCII code
  point escaped to lowercase `\uXXXX` (surrogate pairs above U+FFFF).
- **`Mode::Pretty`**`json_encode($d, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
  | JSON_UNESCAPED_UNICODE)`, the flag set `Composer\Json\JsonFile::encode` uses
  when writing files. 4-space indent, raw `/`, raw UTF-8 (except U+2028 / U+2029,
  which Composer keeps escaped).

Object key order is preserved as-is — callers that need Composer's content-hash
must `ksort` the top level themselves before encoding, exactly as
`Locker::getContentHash` does.

Part of the [`composer-rs`](https://github.com/cresset-tools/composer-rs)
workspace — shared Composer-domain crates for the cresset-tools family
([bougie](https://github.com/cresset-tools/bougie),
[sconce](https://github.com/cresset-tools/sconce)). Extracted from
`bougie-php-json`.

## License

EUPL-1.2.