Allow example
attribute value to be any expression (#354)
This commit is contained in:
parent
a479e6cc0e
commit
e5168819a4
9 changed files with 53 additions and 21 deletions
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
- the `enumset1`/`enumset` optional dependency has been removed, as its `JsonSchema` impl did not actually match the default serialization format of `EnumSet` (https://github.com/GREsau/schemars/pull/339)
|
- the `enumset1`/`enumset` optional dependency has been removed, as its `JsonSchema` impl did not actually match the default serialization format of `EnumSet` (https://github.com/GREsau/schemars/pull/339)
|
||||||
|
|
||||||
### Changed
|
### Changed (_⚠️ breaking changes ⚠️_)
|
||||||
|
|
||||||
- ⚠️ MSRV is now 1.70 ⚠️
|
- MSRV is now 1.70
|
||||||
|
- [The `example` attribute](https://graham.cool/schemars/deriving/attributes/#example) value is now an arbitrary expression, rather than a string literal identifying a function to call. To avoid silent behaviour changes, the expression must not be a string literal where the value can be parsed as a function path - e.g. `#[schemars(example = "foo")]` is now a compile error, but `#[schemars(example = foo())]` is allowed (as is `#[schemars(example = &"foo")]` if you want the the literal string value `"foo"` to be the example).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -298,13 +298,15 @@ Set on a container, variant or field to set the generated schema's `title` and/o
|
||||||
|
|
||||||
<h3 id="example">
|
<h3 id="example">
|
||||||
|
|
||||||
`#[schemars(example = "some::function")]`
|
`#[schemars(example = value)]`
|
||||||
|
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
Set on a container, variant or field to include the result of the given function in the generated schema's `examples`. The function should take no parameters and can return any type that implements serde's `Serialize` trait - it does not need to return the same type as the attached struct/field. This attribute can be repeated to specify multiple examples.
|
Set on a container, variant or field to include the given value in the generated schema's `examples`. The value can be any type that implements serde's `Serialize` trait - it does not need to be the same type as the attached struct/field. This attribute can be repeated to specify multiple examples.
|
||||||
|
|
||||||
To use the result of arbitrary expressions as examples, you can instead use the [`extend`](#extend) attribute, e.g. `[schemars(extend("examples" = ["example string"]))]`.
|
In previous versions of schemars, the value had to be a string literal identifying a defined function that would be called to return the actual example value (similar to the [`default`](#default) attribute). To avoid the new attribute behaviour from silently breaking old consumers, string literals consisting of a single word (e.g. `#[schemars(example = "my_fn")]`) or a path (e.g. `#[schemars(example = "my_mod::my_fn")]`) are currently disallowed. This restriction may be relaxed in a future version of schemars, but for now if you want to include such a string as the literal example value, this can be done by borrowing the value, e.g. `#[schemars(example = &"my_fn")]`. If you instead want to call a function to get the example value (mirrorring the old behaviour), you must use an explicit function call expression, e.g. `#[schemars(example = my_fn())]`.
|
||||||
|
|
||||||
|
Alternatively, to directly set multiple examples without repeating `example = ...` attribute, you can instead use the [`extend`](#extend) attribute, e.g. `#[schemars(extend("examples" = [1, 2, 3]))]`.
|
||||||
|
|
||||||
<h3 id="deprecated">
|
<h3 id="deprecated">
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(Default, JsonSchema, Serialize)]
|
#[derive(Default, JsonSchema, Serialize)]
|
||||||
#[schemars(example = "Struct::default", example = "null")]
|
#[schemars(example = Struct::default(), example = ())]
|
||||||
struct Struct {
|
struct Struct {
|
||||||
#[schemars(example = "eight", example = "null")]
|
#[schemars(example = 4 + 4, example = ())]
|
||||||
foo: i32,
|
foo: i32,
|
||||||
bar: bool,
|
bar: bool,
|
||||||
#[schemars(example = "null")]
|
#[schemars(example = (), example = &"foo")]
|
||||||
baz: Option<&'static str>,
|
baz: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eight() -> i32 {
|
|
||||||
8
|
|
||||||
}
|
|
||||||
|
|
||||||
fn null() {}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples() {
|
fn examples() {
|
||||||
test!(Struct).assert_snapshot();
|
test!(Struct).assert_snapshot();
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
"examples": [
|
"examples": [
|
||||||
null
|
null,
|
||||||
|
"foo"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
"examples": [
|
"examples": [
|
||||||
null
|
null,
|
||||||
|
"foo"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
9
schemars/tests/ui/example_fn.rs
Normal file
9
schemars/tests/ui/example_fn.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
|
#[derive(JsonSchema)]
|
||||||
|
#[schemars(example = "my_fn")]
|
||||||
|
pub struct Struct;
|
||||||
|
|
||||||
|
fn my_fn() {}
|
||||||
|
|
||||||
|
fn main() {}
|
7
schemars/tests/ui/example_fn.stderr
Normal file
7
schemars/tests/ui/example_fn.stderr
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
error: `example` value must be an expression, and string literals that may be interpreted as function paths are currently disallowed to avoid migration errors (this restriction may be relaxed in a future version of schemars).
|
||||||
|
If you want to use the result of a function, use `#[schemars(example = my_fn())]`.
|
||||||
|
Or to use the string literal value, use `#[schemars(example = &"my_fn")]`.
|
||||||
|
--> tests/ui/example_fn.rs:4:22
|
||||||
|
|
|
||||||
|
4 | #[schemars(example = "my_fn")]
|
||||||
|
| ^^^^^^^
|
|
@ -1,5 +1,5 @@
|
||||||
error: Expected a `fn(&mut Schema)` or other value implementing `schemars::transform::Transform`, found `&str`.
|
error: Expected a `fn(&mut Schema)` or other value implementing `schemars::transform::Transform`, found `&str`.
|
||||||
Did you mean `[schemars(transform = x)]`?
|
Did you mean `#[schemars(transform = x)]`?
|
||||||
--> tests/ui/transform_str.rs:4:24
|
--> tests/ui/transform_str.rs:4:24
|
||||||
|
|
|
|
||||||
4 | #[schemars(transform = "x")]
|
4 | #[schemars(transform = "x")]
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub struct CommonAttrs {
|
||||||
pub deprecated: bool,
|
pub deprecated: bool,
|
||||||
pub title: Option<Expr>,
|
pub title: Option<Expr>,
|
||||||
pub description: Option<Expr>,
|
pub description: Option<Expr>,
|
||||||
pub examples: Vec<Path>,
|
pub examples: Vec<Expr>,
|
||||||
pub extensions: Vec<(String, TokenStream)>,
|
pub extensions: Vec<(String, TokenStream)>,
|
||||||
pub transforms: Vec<Expr>,
|
pub transforms: Vec<Expr>,
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,24 @@ impl CommonAttrs {
|
||||||
},
|
},
|
||||||
|
|
||||||
"example" => {
|
"example" => {
|
||||||
self.examples.extend(parse_name_value_lit_str(meta, cx));
|
if let Ok(expr) = parse_name_value_expr(meta, cx) {
|
||||||
|
if let Expr::Lit(ExprLit {
|
||||||
|
lit: Lit::Str(lit_str),
|
||||||
|
..
|
||||||
|
}) = &expr
|
||||||
|
{
|
||||||
|
if lit_str.parse::<Path>().is_ok() {
|
||||||
|
let lit_str_value = lit_str.value();
|
||||||
|
cx.error_spanned_by(&expr, format_args!(
|
||||||
|
"`example` value must be an expression, and string literals that may be interpreted as function paths are currently disallowed to avoid migration errors \
|
||||||
|
(this restriction may be relaxed in a future version of schemars).\n\
|
||||||
|
If you want to use the result of a function, use `#[schemars(example = {lit_str_value}())]`.\n\
|
||||||
|
Or to use the string literal value, use `#[schemars(example = &\"{lit_str_value}\")]`."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.examples.push(expr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"extend" => {
|
"extend" => {
|
||||||
|
@ -114,7 +131,7 @@ impl CommonAttrs {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
&expr,
|
&expr,
|
||||||
format_args!(
|
format_args!(
|
||||||
"Expected a `fn(&mut Schema)` or other value implementing `schemars::transform::Transform`, found `&str`.\nDid you mean `[schemars(transform = {})]`?",
|
"Expected a `fn(&mut Schema)` or other value implementing `schemars::transform::Transform`, found `&str`.\nDid you mean `#[schemars(transform = {})]`?",
|
||||||
lit_str.value()
|
lit_str.value()
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -178,7 +195,7 @@ impl CommonAttrs {
|
||||||
if !self.examples.is_empty() {
|
if !self.examples.is_empty() {
|
||||||
let examples = self.examples.iter().map(|eg| {
|
let examples = self.examples.iter().map(|eg| {
|
||||||
quote! {
|
quote! {
|
||||||
schemars::_private::serde_json::value::to_value(#eg())
|
schemars::_private::serde_json::value::to_value(#eg)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mutators.push(quote! {
|
mutators.push(quote! {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue