# openapi-deref
Lightweight OpenAPI / JSON Schema `$ref` resolver for Rust.
Recursively expands all `$ref` pointers in a `serde_json::Value`, producing a self-contained document with no unresolved references.
## Features
- Resolves internal `$ref` via JSON Pointer (RFC 6901)
- OpenAPI 3.1 compatible: sibling keys alongside `$ref` are merged
- Cycle detection with a visited-path set
- Typed error model: fatal `ResolveError` vs non-fatal `RefError`
- `resolve_strict` for zero-tolerance mode
- Diagnostics: inspect cycles, missing refs, and external refs individually
- Zero dependencies beyond `serde_json` and `thiserror`
## Quick start
```rust
use serde_json::json;
let value = openapi_deref::resolve_strict(&json!({
"components": { "schemas": { "Id": { "type": "integer" } } },
"field": { "$ref": "#/components/schemas/Id" }
})).unwrap();
assert_eq!(value["field"]["type"], "integer");
```
## API levels
| `resolve_strict` | `Result<Value, StrictResolveError>` | Any unresolved ref is unacceptable |
| `resolve` | `Result<ResolvedDoc, ResolveError>` | You need to inspect partial results or diagnostics |
| `resolve_with_root` | `Result<ResolvedDoc, ResolveError>` | The ref lookup root differs from the target value |
## Error handling
Non-fatal ref errors are collected in `ResolvedDoc` and can be inspected:
```rust
use serde_json::json;
use openapi_deref::resolve;
let spec = json!({
"components": { "schemas": {
"Node": { "properties": { "child": { "$ref": "#/components/schemas/Node" } } }
}},
"root": { "$ref": "#/components/schemas/Node" }
});
let doc = resolve(&spec).unwrap();
assert_eq!(doc.cycles().count(), 1);
assert_eq!(doc.missing_refs().count(), 0);
assert_eq!(doc.external_refs().count(), 0);
```
## License
Licensed under either of
- MIT license ([LICENSE-MIT](LICENSE-MIT))
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
at your option.