schemars
schemars copied to clipboard
tag JSON schema for common Rust types with an extension
I'm building a couple of crates that involve turning JSON Schema into Rust types via either a build.rs or a macro. I'm finding that it would be useful to be able to associate the schemas for common Rust types with the actual built-in type. This could apply to pretty much everything in here: https://github.com/GREsau/schemars/blob/master/schemars/src/json_schema_impls
... but take for example Duration; here's how I'd propose the JSON Schema be changed:
},
),
reference: None,
- extensions: {},
+ extensions: {
+ "x-rust-type": String(
+ "std::time::Duration",
+ ),
+ },
},
definitions: {},
}
This would allow consumers to use built-in structures rather than generating new structures. I'd be happy to submit a PR for this work if you'd be amenable to reviewing it.
I like the idea of this, although I'd rather not make the built-in impls "special" by having only them include the rust type in the schema - I'd rather it be done for all types, including custom types that derive JsonSchema.
I believe we could achieve it for all types by leveraging std::any::type_name(). However, this does mean that the values may not be stable, particularly between different versions of rustc. As the docs say:
amongst the strings that
type_name::<Option<String>>()might return are"Option<String>"and"std::option::Option<std::string::String>"
(and it actually currently returns "core::option::Option<alloc::string::String>")
I think this would be acceptable, in which case the resulting schema generation would give you something like:
pub struct MyStruct {
pub str: &'static str,
pub string: String,
pub vec: Vec<MyOtherStruct>,
}
pub struct MyOtherStruct {
pub boxed_int: Box<i32>,
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MyStruct",
"type": "object",
"required": [
"str",
"string",
"vec"
],
"properties": {
"str": {
"type": "string",
"x-rust-type": "&str"
},
"string": {
"type": "string",
"x-rust-type": "alloc::string::String"
},
"vec": {
"type": "array",
"items": {
"$ref": "#/definitions/MyOtherStruct"
},
"x-rust-type": "alloc::vec::Vec<main::MyOtherStruct>"
}
},
"x-rust-type": "main::MyStruct",
"definitions": {
"MyOtherStruct": {
"type": "object",
"required": [
"boxed_int"
],
"properties": {
"boxed_int": {
"type": "integer",
"format": "int32",
"x-rust-type": "alloc::boxed::Box<i32>"
}
},
"x-rust-type": "main::MyOtherStruct"
}
}
}
Having said that, if this were done for all types, maybe it should "see through" references and Boxs etc. Otherwise we could get into strange cases like:
struct MyStruct {
u1: Unit1,
boxed_u2: Box<Unit2>,
u3: Unit3,
boxed_u3: Box<Unit3>,
}
struct Unit1;
struct Unit2;
struct Unit3;
{
...
"properties": {
"u1": {
"$ref": "#/definitions/Unit1"
},
"boxed_u2": {
"$ref": "#/definitions/Unit2"
},
"u3": {
"$ref": "#/definitions/Unit3"
},
"boxed_u3": {
"$ref": "#/definitions/Unit3"
}
},
"definitions": {
"Unit1": {
"type": "null",
"x-rust-type": "Unit1"
},
"Unit2": {
"type": "null",
"x-rust-type": "alloc::boxed::Box<Unit2>"
},
"Unit3": {
"type": "null",
"x-rust-type": "???"
}
}
}
It's not clear whether Unit3's x-rust-type should include Box<> or not, so maybe it should always be omitted
I believe we could achieve it for all types by leveraging
std::any::type_name(). However, this does mean that the values may not be stable, particularly between different versions of rustc. As the docs say:
I think this would make this extension a challenge to use. In particular, I have in mind using it in a JSON schema -> Rust type code generator I've built. Using a stable name would be much more useful. Consider the case where the JSON schema is output from one version of rust and the code generator is run from another version where the private name for the type has changed.
Your point about doing this for derived schemas is a good one. In particular this would be valuable for libraries that include derives JsonSchema impls as part of their public interface. Perhaps they could specify this with an attr like #[schemars(rust_type = "mylib::MyType")]
Regarding Box, I think you're right that it should see through ... just as is done for the type definitions themselves. Having Box<...> in x-rust-type wouldn't be that useful.
@GREsau if you'd be amenable to my proposal, I'd be happy to write a PR for this