Add #[schemars(extend("key" = value))]
attribute (#297)
This commit is contained in:
parent
97b70aa82c
commit
840315b2dd
18 changed files with 527 additions and 26 deletions
|
@ -13,11 +13,11 @@
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"my_optional_string": {
|
"my_optional_string": {
|
||||||
"default": null,
|
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
]
|
],
|
||||||
|
"default": null
|
||||||
},
|
},
|
||||||
"my_struct2": {
|
"my_struct2": {
|
||||||
"$ref": "#/$defs/MyStruct2",
|
"$ref": "#/$defs/MyStruct2",
|
||||||
|
|
101
schemars/tests/expected/extend_enum_adjacent.json
Normal file
101
schemars/tests/expected/extend_enum_adjacent.json
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"title": "Adjacent",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"t": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Unit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"t"
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"t": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"NewType"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"c": true
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"t",
|
||||||
|
"c"
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"t": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Tuple"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"c": {
|
||||||
|
"type": "array",
|
||||||
|
"prefixItems": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minItems": 2,
|
||||||
|
"maxItems": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"t",
|
||||||
|
"c"
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"t": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Struct"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"c": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"i": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"i",
|
||||||
|
"b"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"t",
|
||||||
|
"c"
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
73
schemars/tests/expected/extend_enum_external.json
Normal file
73
schemars/tests/expected/extend_enum_external.json
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"title": "External",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"const": "Unit",
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"NewType": true
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"NewType"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Tuple": {
|
||||||
|
"type": "array",
|
||||||
|
"prefixItems": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minItems": 2,
|
||||||
|
"maxItems": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"Tuple"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Struct": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"i": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"i",
|
||||||
|
"b"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"Struct"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
55
schemars/tests/expected/extend_enum_internal.json
Normal file
55
schemars/tests/expected/extend_enum_internal.json
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"title": "Internal",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"typeProperty": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "Unit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"typeProperty"
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"typeProperty": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "NewType"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"typeProperty"
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"i": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"typeProperty": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "Struct"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"typeProperty",
|
||||||
|
"i",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
46
schemars/tests/expected/extend_enum_untagged.json
Normal file
46
schemars/tests/expected/extend_enum_untagged.json
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"title": "Untagged",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "null",
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"prefixItems": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minItems": 2,
|
||||||
|
"maxItems": 2,
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"i": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"i",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
27
schemars/tests/expected/extend_struct.json
Normal file
27
schemars/tests/expected/extend_struct.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"title": "Struct",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"value": {
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"int": {
|
||||||
|
"type": "overridden",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"value",
|
||||||
|
"int"
|
||||||
|
],
|
||||||
|
"msg": "hello world",
|
||||||
|
"obj": {
|
||||||
|
"array": [
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"3": 3.0,
|
||||||
|
"pi": 3.14
|
||||||
|
}
|
|
@ -35,6 +35,8 @@
|
||||||
},
|
},
|
||||||
"my_tuple": {
|
"my_tuple": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"minItems": 2,
|
||||||
|
"maxItems": 2,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -44,9 +46,7 @@
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"maxItems": 2,
|
|
||||||
"minItems": 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
},
|
},
|
||||||
"my_tuple": {
|
"my_tuple": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"minItems": 2,
|
||||||
|
"maxItems": 2,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -44,9 +46,7 @@
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"maxItems": 2,
|
|
||||||
"minItems": 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
},
|
},
|
||||||
"my_tuple": {
|
"my_tuple": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"minItems": 2,
|
||||||
|
"maxItems": 2,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -46,9 +48,7 @@
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"maxItems": 2,
|
|
||||||
"minItems": 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"maxItems": 2,
|
||||||
|
"minItems": 2,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
@ -40,9 +42,7 @@
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64"
|
"format": "int64"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"minItems": 2,
|
|
||||||
"maxItems": 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"maxItems": 2,
|
||||||
|
"minItems": 2,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
@ -35,9 +37,7 @@
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64"
|
"format": "int64"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"minItems": 2,
|
|
||||||
"maxItems": 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"maxItems": 2,
|
||||||
|
"minItems": 2,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
@ -40,9 +42,7 @@
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64"
|
"format": "int64"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"minItems": 2,
|
|
||||||
"maxItems": 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
96
schemars/tests/extend.rs
Normal file
96
schemars/tests/extend.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
mod util;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde_json::Value;
|
||||||
|
use util::*;
|
||||||
|
|
||||||
|
const THREE: f64 = 3.0;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(JsonSchema)]
|
||||||
|
#[schemars(extend("msg" = concat!("hello ", "world"), "obj" = {"array": [null, ()]}))]
|
||||||
|
#[schemars(extend("3" = THREE), extend("pi" = THREE + 0.14))]
|
||||||
|
struct Struct {
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
value: Value,
|
||||||
|
#[schemars(extend("type" = "overridden"))]
|
||||||
|
int: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doc_comments_struct() -> TestResult {
|
||||||
|
test_default_generated_schema::<Struct>("extend_struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(JsonSchema)]
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
enum External {
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Unit,
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
NewType(Value),
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Tuple(i32, bool),
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Struct { i: i32, b: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doc_comments_enum_external() -> TestResult {
|
||||||
|
test_default_generated_schema::<External>("extend_enum_external")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(JsonSchema)]
|
||||||
|
#[schemars(tag = "typeProperty", extend("foo" = "bar"))]
|
||||||
|
enum Internal {
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Unit,
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
NewType(Value),
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Struct { i: i32, b: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doc_comments_enum_internal() -> TestResult {
|
||||||
|
test_default_generated_schema::<Internal>("extend_enum_internal")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(JsonSchema)]
|
||||||
|
#[schemars(untagged, extend("foo" = "bar"))]
|
||||||
|
enum Untagged {
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Unit,
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
NewType(Value),
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Tuple(i32, bool),
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Struct { i: i32, b: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doc_comments_enum_untagged() -> TestResult {
|
||||||
|
test_default_generated_schema::<Untagged>("extend_enum_untagged")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(JsonSchema)]
|
||||||
|
#[schemars(tag = "t", content = "c", extend("foo" = "bar"))]
|
||||||
|
enum Adjacent {
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Unit,
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
NewType(Value),
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Tuple(i32, bool),
|
||||||
|
#[schemars(extend("foo" = "bar"))]
|
||||||
|
Struct { i: i32, b: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doc_comments_enum_adjacent() -> TestResult {
|
||||||
|
test_default_generated_schema::<Adjacent>("extend_enum_adjacent")
|
||||||
|
}
|
11
schemars/tests/ui/invalid_extend.rs
Normal file
11
schemars/tests/ui/invalid_extend.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
|
#[derive(JsonSchema)]
|
||||||
|
#[schemars(extend(x))]
|
||||||
|
#[schemars(extend("x"))]
|
||||||
|
#[schemars(extend("x" = ))]
|
||||||
|
#[schemars(extend("y" = "ok!", "y" = "duplicated!"), extend("y" = "duplicated!"))]
|
||||||
|
#[schemars(extend("y" = "duplicated!"))]
|
||||||
|
pub struct Struct;
|
||||||
|
|
||||||
|
fn main() {}
|
35
schemars/tests/ui/invalid_extend.stderr
Normal file
35
schemars/tests/ui/invalid_extend.stderr
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
error: expected string literal
|
||||||
|
--> tests/ui/invalid_extend.rs:4:19
|
||||||
|
|
|
||||||
|
4 | #[schemars(extend(x))]
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: expected `=`
|
||||||
|
--> tests/ui/invalid_extend.rs:5:22
|
||||||
|
|
|
||||||
|
5 | #[schemars(extend("x"))]
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: Expected extension value
|
||||||
|
--> tests/ui/invalid_extend.rs:6:25
|
||||||
|
|
|
||||||
|
6 | #[schemars(extend("x" = ))]
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: Duplicate extension key 'y'
|
||||||
|
--> tests/ui/invalid_extend.rs:7:32
|
||||||
|
|
|
||||||
|
7 | #[schemars(extend("y" = "ok!", "y" = "duplicated!"), extend("y" = "duplicated!"))]
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error: Duplicate extension key 'y'
|
||||||
|
--> tests/ui/invalid_extend.rs:7:61
|
||||||
|
|
|
||||||
|
7 | #[schemars(extend("y" = "ok!", "y" = "duplicated!"), extend("y" = "duplicated!"))]
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error: Duplicate extension key 'y'
|
||||||
|
--> tests/ui/invalid_extend.rs:8:19
|
||||||
|
|
|
||||||
|
8 | #[schemars(extend("y" = "duplicated!"))]
|
||||||
|
| ^^^
|
|
@ -10,7 +10,7 @@ use proc_macro2::{Group, Span, TokenStream, TokenTree};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use serde_derive_internals::Ctxt;
|
use serde_derive_internals::Ctxt;
|
||||||
use syn::parse::{self, Parse};
|
use syn::parse::{self, Parse};
|
||||||
use syn::{Meta, MetaNameValue};
|
use syn::{LitStr, Meta, MetaNameValue};
|
||||||
|
|
||||||
// FIXME using the same struct for containers+variants+fields means that
|
// FIXME using the same struct for containers+variants+fields means that
|
||||||
// with/schema_with are accepted (but ignored) on containers, and
|
// with/schema_with are accepted (but ignored) on containers, and
|
||||||
|
@ -26,6 +26,7 @@ pub struct Attrs {
|
||||||
pub repr: Option<syn::Type>,
|
pub repr: Option<syn::Type>,
|
||||||
pub crate_name: Option<syn::Path>,
|
pub crate_name: Option<syn::Path>,
|
||||||
pub is_renamed: bool,
|
pub is_renamed: bool,
|
||||||
|
pub extensions: Vec<(String, TokenStream)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -68,6 +69,7 @@ impl Attrs {
|
||||||
description: self.description.as_ref().and_then(none_if_empty),
|
description: self.description.as_ref().and_then(none_if_empty),
|
||||||
deprecated: self.deprecated,
|
deprecated: self.deprecated,
|
||||||
examples: &self.examples,
|
examples: &self.examples,
|
||||||
|
extensions: &self.extensions,
|
||||||
read_only: false,
|
read_only: false,
|
||||||
write_only: false,
|
write_only: false,
|
||||||
default: None,
|
default: None,
|
||||||
|
@ -162,6 +164,29 @@ impl Attrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Meta::List(m) if m.path.is_ident("extend") && attr_type == "schemars" => {
|
||||||
|
let parser =
|
||||||
|
syn::punctuated::Punctuated::<Extension, Token![,]>::parse_terminated;
|
||||||
|
match m.parse_args_with(parser) {
|
||||||
|
Ok(extensions) => {
|
||||||
|
for extension in extensions {
|
||||||
|
let key = extension.key.value();
|
||||||
|
// This is O(n^2) but should be fine with the typically small number of extensions.
|
||||||
|
// If this does become a problem, it can be changed to use IndexMap, or a separate Map with cloned keys.
|
||||||
|
if self.extensions.iter().any(|e| e.0 == key) {
|
||||||
|
errors.error_spanned_by(
|
||||||
|
extension.key,
|
||||||
|
format!("Duplicate extension key '{}'", key),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.extensions.push((key, extension.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => errors.syn_error(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ if ignore_errors => {}
|
_ if ignore_errors => {}
|
||||||
|
|
||||||
Meta::List(m) if m.path.is_ident("inner") && attr_type == "schemars" => {
|
Meta::List(m) if m.path.is_ident("inner") && attr_type == "schemars" => {
|
||||||
|
@ -198,7 +223,8 @@ impl Attrs {
|
||||||
repr: None,
|
repr: None,
|
||||||
crate_name: None,
|
crate_name: None,
|
||||||
is_renamed: _,
|
is_renamed: _,
|
||||||
} if examples.is_empty())
|
extensions,
|
||||||
|
} if examples.is_empty() && extensions.is_empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,3 +348,27 @@ fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree {
|
||||||
token.set_span(span);
|
token.set_span(span);
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Extension {
|
||||||
|
key: LitStr,
|
||||||
|
value: TokenStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Extension {
|
||||||
|
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let key = input.parse::<LitStr>()?;
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
let mut value = TokenStream::new();
|
||||||
|
|
||||||
|
while !input.is_empty() && !input.peek(Token![,]) {
|
||||||
|
value.extend([input.parse::<TokenTree>()?]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.is_empty() {
|
||||||
|
return Err(syn::Error::new(input.span(), "Expected extension value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Extension { key, value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub struct SchemaMetadata<'a> {
|
||||||
pub write_only: bool,
|
pub write_only: bool,
|
||||||
pub examples: &'a [syn::Path],
|
pub examples: &'a [syn::Path],
|
||||||
pub default: Option<TokenStream>,
|
pub default: Option<TokenStream>,
|
||||||
|
pub extensions: &'a [(String, TokenStream)],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SchemaMetadata<'a> {
|
impl<'a> SchemaMetadata<'a> {
|
||||||
|
@ -74,6 +75,12 @@ impl<'a> SchemaMetadata<'a> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (k, v) in self.extensions {
|
||||||
|
setters.push(quote! {
|
||||||
|
obj.insert(#k.to_owned(), schemars::_serde_json::json!(#v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setters
|
setters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,13 +232,13 @@ fn expr_for_internal_tagged_enum<'a>(
|
||||||
let name = variant.name();
|
let name = variant.name();
|
||||||
|
|
||||||
let mut schema_expr = expr_for_internal_tagged_enum_variant(variant, deny_unknown_fields);
|
let mut schema_expr = expr_for_internal_tagged_enum_variant(variant, deny_unknown_fields);
|
||||||
variant.attrs.as_metadata().apply_to_schema(&mut schema_expr);
|
schema_expr = quote!({
|
||||||
|
|
||||||
quote!({
|
|
||||||
let mut schema = #schema_expr;
|
let mut schema = #schema_expr;
|
||||||
schemars::_private::apply_internal_enum_variant_tag(&mut schema, #tag_name, #name, #deny_unknown_fields);
|
schemars::_private::apply_internal_enum_variant_tag(&mut schema, #tag_name, #name, #deny_unknown_fields);
|
||||||
schema
|
schema
|
||||||
})
|
});
|
||||||
|
variant.attrs.as_metadata().apply_to_schema(&mut schema_expr);
|
||||||
|
schema_expr
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue