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)
|
||||
|
||||
### 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
|
||||
|
||||
|
|
|
@ -298,13 +298,15 @@ Set on a container, variant or field to set the generated schema's `title` and/o
|
|||
|
||||
<h3 id="example">
|
||||
|
||||
`#[schemars(example = "some::function")]`
|
||||
`#[schemars(example = value)]`
|
||||
|
||||
</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">
|
||||
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
#[derive(Default, JsonSchema, Serialize)]
|
||||
#[schemars(example = "Struct::default", example = "null")]
|
||||
#[schemars(example = Struct::default(), example = ())]
|
||||
struct Struct {
|
||||
#[schemars(example = "eight", example = "null")]
|
||||
#[schemars(example = 4 + 4, example = ())]
|
||||
foo: i32,
|
||||
bar: bool,
|
||||
#[schemars(example = "null")]
|
||||
#[schemars(example = (), example = &"foo")]
|
||||
baz: Option<&'static str>,
|
||||
}
|
||||
|
||||
fn eight() -> i32 {
|
||||
8
|
||||
}
|
||||
|
||||
fn null() {}
|
||||
|
||||
#[test]
|
||||
fn examples() {
|
||||
test!(Struct).assert_snapshot();
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
"null"
|
||||
],
|
||||
"examples": [
|
||||
null
|
||||
null,
|
||||
"foo"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
"null"
|
||||
],
|
||||
"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`.
|
||||
Did you mean `[schemars(transform = x)]`?
|
||||
Did you mean `#[schemars(transform = x)]`?
|
||||
--> tests/ui/transform_str.rs:4:24
|
||||
|
|
||||
4 | #[schemars(transform = "x")]
|
||||
|
|
|
@ -21,7 +21,7 @@ pub struct CommonAttrs {
|
|||
pub deprecated: bool,
|
||||
pub title: Option<Expr>,
|
||||
pub description: Option<Expr>,
|
||||
pub examples: Vec<Path>,
|
||||
pub examples: Vec<Expr>,
|
||||
pub extensions: Vec<(String, TokenStream)>,
|
||||
pub transforms: Vec<Expr>,
|
||||
}
|
||||
|
@ -84,7 +84,24 @@ impl CommonAttrs {
|
|||
},
|
||||
|
||||
"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" => {
|
||||
|
@ -114,7 +131,7 @@ impl CommonAttrs {
|
|||
cx.error_spanned_by(
|
||||
&expr,
|
||||
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()
|
||||
),
|
||||
)
|
||||
|
@ -178,7 +195,7 @@ impl CommonAttrs {
|
|||
if !self.examples.is_empty() {
|
||||
let examples = self.examples.iter().map(|eg| {
|
||||
quote! {
|
||||
schemars::_private::serde_json::value::to_value(#eg())
|
||||
schemars::_private::serde_json::value::to_value(#eg)
|
||||
}
|
||||
});
|
||||
mutators.push(quote! {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue