schemars/lib.rs
1#![forbid(unsafe_code)]
2/*!
3Generate JSON Schema documents from Rust code
4
5## Basic Usage
6
7If you don't really care about the specifics, the easiest way to generate a JSON schema for your types is to `#[derive(JsonSchema)]` and use the `schema_for!` macro. All fields of the type must also implement `JsonSchema` - Schemars implements this for many standard library types.
8
9```rust
10use schemars::{schema_for, JsonSchema};
11
12#[derive(JsonSchema)]
13pub struct MyStruct {
14 pub my_int: i32,
15 pub my_bool: bool,
16 pub my_nullable_enum: Option<MyEnum>,
17}
18
19#[derive(JsonSchema)]
20pub enum MyEnum {
21 StringNewType(String),
22 StructVariant { floats: Vec<f32> },
23}
24
25let schema = schema_for!(MyStruct);
26println!("{}", serde_json::to_string_pretty(&schema).unwrap());
27```
28
29<details>
30<summary>Click to see the output JSON schema...</summary>
31
32```json
33{
34 "$schema": "http://json-schema.org/draft-07/schema#",
35 "title": "MyStruct",
36 "type": "object",
37 "required": [
38 "my_bool",
39 "my_int"
40 ],
41 "properties": {
42 "my_bool": {
43 "type": "boolean"
44 },
45 "my_int": {
46 "type": "integer",
47 "format": "int32"
48 },
49 "my_nullable_enum": {
50 "anyOf": [
51 {
52 "$ref": "#/definitions/MyEnum"
53 },
54 {
55 "type": "null"
56 }
57 ]
58 }
59 },
60 "definitions": {
61 "MyEnum": {
62 "anyOf": [
63 {
64 "type": "object",
65 "required": [
66 "StringNewType"
67 ],
68 "properties": {
69 "StringNewType": {
70 "type": "string"
71 }
72 },
73 "additionalProperties": false
74 },
75 {
76 "type": "object",
77 "required": [
78 "StructVariant"
79 ],
80 "properties": {
81 "StructVariant": {
82 "type": "object",
83 "required": [
84 "floats"
85 ],
86 "properties": {
87 "floats": {
88 "type": "array",
89 "items": {
90 "type": "number",
91 "format": "float"
92 }
93 }
94 }
95 }
96 },
97 "additionalProperties": false
98 }
99 ]
100 }
101 }
102}
103```
104</details>
105
106### Serde Compatibility
107
108One of the main aims of this library is compatibility with [Serde](https://github.com/serde-rs/serde). Any generated schema *should* match how [serde_json](https://github.com/serde-rs/json) would serialize/deserialize to/from JSON. To support this, Schemars will check for any `#[serde(...)]` attributes on types that derive `JsonSchema`, and adjust the generated schema accordingly.
109
110```rust
111use schemars::{schema_for, JsonSchema};
112use serde::{Deserialize, Serialize};
113
114#[derive(Deserialize, Serialize, JsonSchema)]
115#[serde(rename_all = "camelCase", deny_unknown_fields)]
116pub struct MyStruct {
117 #[serde(rename = "myNumber")]
118 pub my_int: i32,
119 pub my_bool: bool,
120 #[serde(default)]
121 pub my_nullable_enum: Option<MyEnum>,
122}
123
124#[derive(Deserialize, Serialize, JsonSchema)]
125#[serde(untagged)]
126pub enum MyEnum {
127 StringNewType(String),
128 StructVariant { floats: Vec<f32> },
129}
130
131let schema = schema_for!(MyStruct);
132println!("{}", serde_json::to_string_pretty(&schema).unwrap());
133```
134
135<details>
136<summary>Click to see the output JSON schema...</summary>
137
138```json
139{
140 "$schema": "http://json-schema.org/draft-07/schema#",
141 "title": "MyStruct",
142 "type": "object",
143 "required": [
144 "myBool",
145 "myNumber"
146 ],
147 "properties": {
148 "myBool": {
149 "type": "boolean"
150 },
151 "myNullableEnum": {
152 "default": null,
153 "anyOf": [
154 {
155 "$ref": "#/definitions/MyEnum"
156 },
157 {
158 "type": "null"
159 }
160 ]
161 },
162 "myNumber": {
163 "type": "integer",
164 "format": "int32"
165 }
166 },
167 "additionalProperties": false,
168 "definitions": {
169 "MyEnum": {
170 "anyOf": [
171 {
172 "type": "string"
173 },
174 {
175 "type": "object",
176 "required": [
177 "floats"
178 ],
179 "properties": {
180 "floats": {
181 "type": "array",
182 "items": {
183 "type": "number",
184 "format": "float"
185 }
186 }
187 }
188 }
189 ]
190 }
191 }
192}
193```
194</details>
195
196`#[serde(...)]` attributes can be overriden using `#[schemars(...)]` attributes, which behave identically (e.g. `#[schemars(rename_all = "camelCase")]`). You may find this useful if you want to change the generated schema without affecting Serde's behaviour, or if you're just not using Serde.
197
198### Schema from Example Value
199
200If you want a schema for a type that can't/doesn't implement `JsonSchema`, but does implement `serde::Serialize`, then you can generate a JSON schema from a value of that type. However, this schema will generally be less precise than if the type implemented `JsonSchema` - particularly when it involves enums, since schemars will not make any assumptions about the structure of an enum based on a single variant.
201
202```rust
203use schemars::schema_for_value;
204use serde::Serialize;
205
206#[derive(Serialize)]
207pub struct MyStruct {
208 pub my_int: i32,
209 pub my_bool: bool,
210 pub my_nullable_enum: Option<MyEnum>,
211}
212
213#[derive(Serialize)]
214pub enum MyEnum {
215 StringNewType(String),
216 StructVariant { floats: Vec<f32> },
217}
218
219let schema = schema_for_value!(MyStruct {
220 my_int: 123,
221 my_bool: true,
222 my_nullable_enum: Some(MyEnum::StringNewType("foo".to_string()))
223});
224println!("{}", serde_json::to_string_pretty(&schema).unwrap());
225```
226
227<details>
228<summary>Click to see the output JSON schema...</summary>
229
230```json
231{
232 "$schema": "http://json-schema.org/draft-07/schema#",
233 "title": "MyStruct",
234 "examples": [
235 {
236 "my_bool": true,
237 "my_int": 123,
238 "my_nullable_enum": {
239 "StringNewType": "foo"
240 }
241 }
242 ],
243 "type": "object",
244 "properties": {
245 "my_bool": {
246 "type": "boolean"
247 },
248 "my_int": {
249 "type": "integer"
250 },
251 "my_nullable_enum": true
252 }
253}
254```
255</details>
256
257## Feature Flags
258- `derive` (enabled by default) - provides `#[derive(JsonSchema)]` macro
259- `impl_json_schema` - implements `JsonSchema` for Schemars types themselves
260- `preserve_order` - keep the order of struct fields in `Schema` and `SchemaObject`
261
262Schemars can implement `JsonSchema` on types from several popular crates, enabled via feature flags (dependency versions are shown in brackets):
263- `chrono` - [chrono](https://crates.io/crates/chrono) (^0.4)
264- `indexmap1` - [indexmap](https://crates.io/crates/indexmap) (^1.2)
265- `either` - [either](https://crates.io/crates/either) (^1.3)
266- `uuid08` - [uuid](https://crates.io/crates/uuid) (^0.8)
267- `uuid1` - [uuid](https://crates.io/crates/uuid) (^1.0)
268- `smallvec` - [smallvec](https://crates.io/crates/smallvec) (^1.0)
269- `arrayvec05` - [arrayvec](https://crates.io/crates/arrayvec) (^0.5)
270- `arrayvec07` - [arrayvec](https://crates.io/crates/arrayvec) (^0.7)
271- `url` - [url](https://crates.io/crates/url) (^2.0)
272- `bytes` - [bytes](https://crates.io/crates/bytes) (^1.0)
273- `enumset` - [enumset](https://crates.io/crates/enumset) (^1.0)
274- `rust_decimal` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0)
275- `bigdecimal` - [bigdecimal](https://crates.io/crates/bigdecimal) (^0.3)
276
277For example, to implement `JsonSchema` on types from `chrono`, enable it as a feature in the `schemars` dependency in your `Cargo.toml` like so:
278
279```toml
280[dependencies]
281schemars = { version = "0.8", features = ["chrono"] }
282```
283
284```
285*/
286
287/// The map type used by schemars types.
288///
289/// Currently a `BTreeMap` or `IndexMap` can be used, but this may change to a different implementation
290/// with a similar interface in a future version of schemars.
291/// The `IndexMap` will be used when the `preserve_order` feature flag is set.
292#[cfg(not(feature = "preserve_order"))]
293pub type Map<K, V> = std::collections::BTreeMap<K, V>;
294#[cfg(feature = "preserve_order")]
295pub type Map<K, V> = indexmap::IndexMap<K, V>;
296/// The set type used by schemars types.
297///
298/// Currently a `BTreeSet`, but this may change to a different implementation
299/// with a similar interface in a future version of schemars.
300pub type Set<T> = std::collections::BTreeSet<T>;
301
302/// A view into a single entry in a map, which may either be vacant or occupied.
303//
304/// This is constructed from the `entry` method on `BTreeMap` or `IndexMap`,
305/// depending on whether the `preserve_order` feature flag is set.
306#[cfg(not(feature = "preserve_order"))]
307pub type MapEntry<'a, K, V> = std::collections::btree_map::Entry<'a, K, V>;
308#[cfg(feature = "preserve_order")]
309pub type MapEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>;
310
311mod flatten;
312mod json_schema_impls;
313mod ser;
314#[macro_use]
315mod macros;
316
317/// This module is only public for use by `schemars_derive`. It should not need to be used by code
318/// outside of `schemars`, and should not be considered part of the public API.
319#[doc(hidden)]
320pub mod _private;
321pub mod gen;
322pub mod schema;
323pub mod visit;
324
325#[cfg(feature = "schemars_derive")]
326extern crate schemars_derive;
327#[cfg(feature = "schemars_derive")]
328pub use schemars_derive::*;
329
330// Export serde_json so schemars_derive can use it
331#[doc(hidden)]
332pub use serde_json as _serde_json;
333
334use schema::Schema;
335
336/// A type which can be described as a JSON Schema document.
337///
338/// This is implemented for many Rust primitive and standard library types.
339///
340/// This can also be automatically derived on most custom types with `#[derive(JsonSchema)]`.
341///
342/// # Example
343/// ```
344/// use schemars::{schema_for, JsonSchema};
345///
346/// #[derive(JsonSchema)]
347/// struct MyStruct {
348/// foo: i32,
349/// }
350///
351/// let my_schema = schema_for!(MyStruct);
352/// ```
353pub trait JsonSchema {
354 /// Whether JSON Schemas generated for this type should be re-used where possible using the `$ref` keyword.
355 ///
356 /// For trivial types (such as primitives), this should return `false`. For more complex types, it should return `true`.
357 /// For recursive types, this **must** return `true` to prevent infinite cycles when generating schemas.
358 ///
359 /// By default, this returns `true`.
360 fn is_referenceable() -> bool {
361 true
362 }
363
364 /// The name of the generated JSON Schema.
365 ///
366 /// This is used as the title for root schemas, and the key within the root's `definitions` property for subschemas.
367 fn schema_name() -> String;
368
369 /// Generates a JSON Schema for this type.
370 ///
371 /// If the returned schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
372 /// add them to the [`SchemaGenerator`](gen::SchemaGenerator)'s schema definitions.
373 ///
374 /// This should not return a `$ref` schema.
375 fn json_schema(gen: &mut gen::SchemaGenerator) -> Schema;
376
377 // TODO document and bring into public API?
378 #[doc(hidden)]
379 fn _schemars_private_non_optional_json_schema(gen: &mut gen::SchemaGenerator) -> Schema {
380 Self::json_schema(gen)
381 }
382
383 // TODO document and bring into public API?
384 #[doc(hidden)]
385 fn _schemars_private_is_option() -> bool {
386 false
387 }
388}
389
390#[cfg(test)]
391pub mod tests {
392 use super::*;
393
394 pub fn schema_object_for<T: JsonSchema>() -> schema::SchemaObject {
395 schema_object(schema_for::<T>())
396 }
397
398 pub fn schema_for<T: JsonSchema>() -> schema::Schema {
399 let mut gen = gen::SchemaGenerator::default();
400 T::json_schema(&mut gen)
401 }
402
403 pub fn schema_object(schema: schema::Schema) -> schema::SchemaObject {
404 match schema {
405 schema::Schema::Object(o) => o,
406 s => panic!("Schema was not an object: {:?}", s),
407 }
408 }
409}