oxidecomputer / oxidecomputer/typify
defer resolution of references
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 898
- Forks
- 114
- Avg merge
- 4h 18m
- Merged PRs (30d)
- 14
Description
As of #405 the handling of subschemas has changed significantly. In particular, we try to resolve and merge subschemas in order to produce better, higher fidelity data types. We resolve references in order to--for example--merge types. This, however, opens the door to infinite recursion if we follow references around cyclic types. Here's a trivial (and extremely painful to get right) schema that demonstrates the problem:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"a": {
"type": "object",
"properties": {
"other": {
"$ref": "#/definitions/b"
}
}
},
"b": {
"type": "object",
"properties": {
"other": {
"$ref": "#/definitions/a"
}
}
},
"ab": {
"allOf": [
{
"$ref": "#/definitions/a"
},
{
"$ref": "#/definitions/b"
}
]
}
}
}
There are many other types of cycles we can fall into, but I believe we can significantly ameliorate the problem (and perhaps solve it entirely) by taking a couple of steps:
1. Defer resolution of references as late as possible.
There are two top-level schema structures that we need to process before we can start type conversion/inference: allOf and anyOf (the former we do today; the latter we do not). We have some sort of handling of all other structures, but those need work. I'm going to focus exclusively on allOf here; for anyOf see #414 since that can become more or less a special case.
We eagerly merge schemas which includes resolving references. In part we do this to know what merged schemas are valid. But we can be lazier about that! A top-level allOf that's not valid? That means the schema specified a type or dependency that was never going to valid... in which case it's fine to generate a type that could never be valid!
2. Introduce a new type of dependency
The input schema contains dependencies; we could add our own type of dependency that is based on content rather than names. In the example above we could start to generate the type for ab and say: make a placeholder for { allOf: [a, b] } and when in the next step we tried to resolve the property other we would see that it's type needed to be a reference. This would assume some sort of canonical representation or flexible matching such that { allOf: [a, b] } was equivalent to { allOf: [b, a] }.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Trace the existing allOf handling and the type conversion or inference path; the issue names no files or tests, so first identify those entry points and reproduce the cyclic schema shown above. Done should defer reference resolution until necessary and handle the content-based allOf dependency without infinite recursion, with coverage for the example.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100