diff --git a/CHANGELOG.md b/CHANGELOG.md
index a84c477..7b0adea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/docs/_includes/attributes.md b/docs/_includes/attributes.md
index 37f6833..1497b0f 100644
--- a/docs/_includes/attributes.md
+++ b/docs/_includes/attributes.md
@@ -298,13 +298,15 @@ Set on a container, variant or field to set the generated schema's `title` and/o
-`#[schemars(example = "some::function")]`
+`#[schemars(example = value)]`
-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]))]`.
diff --git a/schemars/tests/integration/examples.rs b/schemars/tests/integration/examples.rs
index 43b7419..427a666 100644
--- a/schemars/tests/integration/examples.rs
+++ b/schemars/tests/integration/examples.rs
@@ -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();
diff --git a/schemars/tests/integration/snapshots/schemars/tests/integration/examples.rs~examples.de.json b/schemars/tests/integration/snapshots/schemars/tests/integration/examples.rs~examples.de.json
index b891aa0..da04166 100644
--- a/schemars/tests/integration/snapshots/schemars/tests/integration/examples.rs~examples.de.json
+++ b/schemars/tests/integration/snapshots/schemars/tests/integration/examples.rs~examples.de.json
@@ -20,7 +20,8 @@
"null"
],
"examples": [
- null
+ null,
+ "foo"
]
}
},
diff --git a/schemars/tests/integration/snapshots/schemars/tests/integration/examples.rs~examples.ser.json b/schemars/tests/integration/snapshots/schemars/tests/integration/examples.rs~examples.ser.json
index 97465db..7de3378 100644
--- a/schemars/tests/integration/snapshots/schemars/tests/integration/examples.rs~examples.ser.json
+++ b/schemars/tests/integration/snapshots/schemars/tests/integration/examples.rs~examples.ser.json
@@ -20,7 +20,8 @@
"null"
],
"examples": [
- null
+ null,
+ "foo"
]
}
},
diff --git a/schemars/tests/ui/example_fn.rs b/schemars/tests/ui/example_fn.rs
new file mode 100644
index 0000000..c4921c5
--- /dev/null
+++ b/schemars/tests/ui/example_fn.rs
@@ -0,0 +1,9 @@
+use schemars::JsonSchema;
+
+#[derive(JsonSchema)]
+#[schemars(example = "my_fn")]
+pub struct Struct;
+
+fn my_fn() {}
+
+fn main() {}
diff --git a/schemars/tests/ui/example_fn.stderr b/schemars/tests/ui/example_fn.stderr
new file mode 100644
index 0000000..92a34f3
--- /dev/null
+++ b/schemars/tests/ui/example_fn.stderr
@@ -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")]
+ | ^^^^^^^
diff --git a/schemars/tests/ui/transform_str.stderr b/schemars/tests/ui/transform_str.stderr
index 6ee3698..50cf7b9 100644
--- a/schemars/tests/ui/transform_str.stderr
+++ b/schemars/tests/ui/transform_str.stderr
@@ -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")]
diff --git a/schemars_derive/src/attr/mod.rs b/schemars_derive/src/attr/mod.rs
index 14fd6eb..37350d3 100644
--- a/schemars_derive/src/attr/mod.rs
+++ b/schemars_derive/src/attr/mod.rs
@@ -21,7 +21,7 @@ pub struct CommonAttrs {
pub deprecated: bool,
pub title: Option,
pub description: Option,
- pub examples: Vec,
+ pub examples: Vec,
pub extensions: Vec<(String, TokenStream)>,
pub transforms: Vec,
}
@@ -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::().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! {