Add schema_id(), handles different types with the same name (#247)
This commit is contained in:
parent
53bb51cb25
commit
342b2dff33
26 changed files with 415 additions and 34 deletions
|
@ -11,6 +11,8 @@ use crate::schema::*;
|
|||
use crate::{visit::*, JsonSchema, Map};
|
||||
use dyn_clone::DynClone;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::{any::Any, collections::HashSet, fmt::Debug};
|
||||
|
||||
/// Settings to customize how Schemas are generated.
|
||||
|
@ -149,7 +151,9 @@ impl SchemaSettings {
|
|||
pub struct SchemaGenerator {
|
||||
settings: SchemaSettings,
|
||||
definitions: Map<String, Schema>,
|
||||
pending_schema_names: HashSet<String>,
|
||||
pending_schema_ids: HashSet<Cow<'static, str>>,
|
||||
schema_id_to_name: HashMap<Cow<'static, str>, String>,
|
||||
used_schema_names: HashSet<String>,
|
||||
}
|
||||
|
||||
impl Clone for SchemaGenerator {
|
||||
|
@ -157,7 +161,9 @@ impl Clone for SchemaGenerator {
|
|||
Self {
|
||||
settings: self.settings.clone(),
|
||||
definitions: self.definitions.clone(),
|
||||
pending_schema_names: HashSet::new(),
|
||||
pending_schema_ids: HashSet::new(),
|
||||
schema_id_to_name: HashMap::new(),
|
||||
used_schema_names: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,27 +219,54 @@ impl SchemaGenerator {
|
|||
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
|
||||
/// add them to the `SchemaGenerator`'s schema definitions.
|
||||
pub fn subschema_for<T: ?Sized + JsonSchema>(&mut self) -> Schema {
|
||||
let name = T::schema_name();
|
||||
let id = T::schema_id();
|
||||
let return_ref = T::is_referenceable()
|
||||
&& (!self.settings.inline_subschemas || self.pending_schema_names.contains(&name));
|
||||
&& (!self.settings.inline_subschemas || self.pending_schema_ids.contains(&id));
|
||||
|
||||
if return_ref {
|
||||
let reference = format!("{}{}", self.settings().definitions_path, name);
|
||||
let name = match self.schema_id_to_name.get(&id).cloned() {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
let base_name = T::schema_name();
|
||||
let mut name = String::new();
|
||||
|
||||
if self.used_schema_names.contains(&base_name) {
|
||||
for i in 2.. {
|
||||
name = format!("{}{}", base_name, i);
|
||||
if !self.used_schema_names.contains(&name) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
name = base_name;
|
||||
}
|
||||
|
||||
self.used_schema_names.insert(name.clone());
|
||||
self.schema_id_to_name.insert(id.clone(), name.clone());
|
||||
name
|
||||
}
|
||||
};
|
||||
|
||||
let reference = format!("{}{}", self.settings.definitions_path, name);
|
||||
if !self.definitions.contains_key(&name) {
|
||||
self.insert_new_subschema_for::<T>(name);
|
||||
self.insert_new_subschema_for::<T>(name, id);
|
||||
}
|
||||
Schema::new_ref(reference)
|
||||
} else {
|
||||
self.json_schema_internal::<T>(&name)
|
||||
self.json_schema_internal::<T>(id)
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_new_subschema_for<T: ?Sized + JsonSchema>(&mut self, name: String) {
|
||||
fn insert_new_subschema_for<T: ?Sized + JsonSchema>(
|
||||
&mut self,
|
||||
name: String,
|
||||
id: Cow<'static, str>,
|
||||
) {
|
||||
let dummy = Schema::Bool(false);
|
||||
// insert into definitions BEFORE calling json_schema to avoid infinite recursion
|
||||
self.definitions.insert(name.clone(), dummy);
|
||||
|
||||
let schema = self.json_schema_internal::<T>(&name);
|
||||
let schema = self.json_schema_internal::<T>(id);
|
||||
|
||||
self.definitions.insert(name, schema);
|
||||
}
|
||||
|
@ -274,9 +307,8 @@ impl SchemaGenerator {
|
|||
/// add them to the `SchemaGenerator`'s schema definitions and include them in the returned `SchemaObject`'s
|
||||
/// [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
|
||||
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> RootSchema {
|
||||
let name = T::schema_name();
|
||||
let mut schema = self.json_schema_internal::<T>(&name).into_object();
|
||||
schema.metadata().title.get_or_insert(name);
|
||||
let mut schema = self.json_schema_internal::<T>(T::schema_id()).into_object();
|
||||
schema.metadata().title.get_or_insert_with(T::schema_name);
|
||||
let mut root = RootSchema {
|
||||
meta_schema: self.settings.meta_schema.clone(),
|
||||
definitions: self.definitions.clone(),
|
||||
|
@ -295,9 +327,8 @@ impl SchemaGenerator {
|
|||
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
|
||||
/// include them in the returned `SchemaObject`'s [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
|
||||
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> RootSchema {
|
||||
let name = T::schema_name();
|
||||
let mut schema = self.json_schema_internal::<T>(&name).into_object();
|
||||
schema.metadata().title.get_or_insert(name);
|
||||
let mut schema = self.json_schema_internal::<T>(T::schema_id()).into_object();
|
||||
schema.metadata().title.get_or_insert_with(T::schema_name);
|
||||
let mut root = RootSchema {
|
||||
meta_schema: self.settings.meta_schema,
|
||||
definitions: self.definitions,
|
||||
|
@ -418,29 +449,29 @@ impl SchemaGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
fn json_schema_internal<T: ?Sized + JsonSchema>(&mut self, name: &str) -> Schema {
|
||||
fn json_schema_internal<T: ?Sized + JsonSchema>(&mut self, id: Cow<'static, str>) -> Schema {
|
||||
struct PendingSchemaState<'a> {
|
||||
gen: &'a mut SchemaGenerator,
|
||||
name: &'a str,
|
||||
id: Cow<'static, str>,
|
||||
did_add: bool,
|
||||
}
|
||||
|
||||
impl<'a> PendingSchemaState<'a> {
|
||||
fn new(gen: &'a mut SchemaGenerator, name: &'a str) -> Self {
|
||||
let did_add = gen.pending_schema_names.insert(name.to_owned());
|
||||
Self { gen, name, did_add }
|
||||
fn new(gen: &'a mut SchemaGenerator, id: Cow<'static, str>) -> Self {
|
||||
let did_add = gen.pending_schema_ids.insert(id.clone());
|
||||
Self { gen, id, did_add }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PendingSchemaState<'_> {
|
||||
fn drop(&mut self) {
|
||||
if self.did_add {
|
||||
self.gen.pending_schema_names.remove(self.name);
|
||||
self.gen.pending_schema_ids.remove(&self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pss = PendingSchemaState::new(self, name);
|
||||
let pss = PendingSchemaState::new(self, id);
|
||||
T::json_schema(pss.gen)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
|
||||
// Does not require T: JsonSchema.
|
||||
impl<T> JsonSchema for [T; 0] {
|
||||
|
@ -10,6 +11,10 @@ impl<T> JsonSchema for [T; 0] {
|
|||
"EmptyArray".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("[]")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::Array.into()),
|
||||
|
@ -33,6 +38,11 @@ macro_rules! array_impls {
|
|||
format!("Array_size_{}_of_{}", $len, T::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(
|
||||
format!("[{}; {}]", $len, T::schema_id()))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::Array.into()),
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::schema::*;
|
|||
use crate::JsonSchema;
|
||||
use chrono::prelude::*;
|
||||
use serde_json::json;
|
||||
use std::borrow::Cow;
|
||||
|
||||
impl JsonSchema for Weekday {
|
||||
no_ref_schema!();
|
||||
|
@ -11,6 +12,10 @@ impl JsonSchema for Weekday {
|
|||
"Weekday".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("chrono::Weekday")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
@ -41,6 +46,10 @@ macro_rules! formatted_string_impl {
|
|||
stringify!($ty).to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed(stringify!(chrono::$ty))
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::gen::SchemaGenerator;
|
|||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use serde_json::json;
|
||||
use std::borrow::Cow;
|
||||
use std::ops::{Bound, Range, RangeInclusive};
|
||||
|
||||
impl<T: JsonSchema> JsonSchema for Option<T> {
|
||||
|
@ -11,6 +12,10 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
|
|||
format!("Nullable_{}", T::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("Option<{}>", T::schema_id()))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut schema = gen.subschema_for::<T>();
|
||||
if gen.settings().option_add_null_type {
|
||||
|
@ -69,6 +74,10 @@ impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
|
|||
format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("Result<{}, {}>", T::schema_id(), E::schema_id()))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut ok_schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
|
@ -99,6 +108,10 @@ impl<T: JsonSchema> JsonSchema for Bound<T> {
|
|||
format!("Bound_of_{}", T::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("Bound<{}>", T::schema_id()))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut included_schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
|
@ -139,6 +152,10 @@ impl<T: JsonSchema> JsonSchema for Range<T> {
|
|||
format!("Range_of_{}", T::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("Range<{}>", T::schema_id()))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
|
||||
macro_rules! decimal_impl {
|
||||
($type:ty) => {
|
||||
|
@ -11,6 +12,10 @@ macro_rules! decimal_impl {
|
|||
"Decimal".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed($name)
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::gen::SchemaGenerator;
|
|||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use either::Either;
|
||||
use std::borrow::Cow;
|
||||
|
||||
impl<L: JsonSchema, R: JsonSchema> JsonSchema for Either<L, R> {
|
||||
no_ref_schema!();
|
||||
|
@ -10,6 +11,14 @@ impl<L: JsonSchema, R: JsonSchema> JsonSchema for Either<L, R> {
|
|||
format!("Either_{}_or_{}", L::schema_name(), R::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(format!(
|
||||
"either::Either<{}, {}>",
|
||||
L::schema_id(),
|
||||
R::schema_id()
|
||||
))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut schema = SchemaObject::default();
|
||||
schema.subschemas().any_of = Some(vec![gen.subschema_for::<L>(), gen.subschema_for::<R>()]);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::{CStr, CString, OsStr, OsString};
|
||||
|
||||
impl JsonSchema for OsString {
|
||||
|
@ -8,6 +9,10 @@ impl JsonSchema for OsString {
|
|||
"OsString".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("std::ffi::OsString")
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut unix_schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
|
||||
macro_rules! map_impl {
|
||||
($($desc:tt)+) => {
|
||||
|
@ -14,6 +15,10 @@ macro_rules! map_impl {
|
|||
format!("Map_of_{}", V::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("Map<{}>", V::schema_id()))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let subschema = gen.subschema_for::<V>();
|
||||
SchemaObject {
|
||||
|
|
|
@ -17,6 +17,10 @@ macro_rules! forward_impl {
|
|||
<$target>::schema_name()
|
||||
}
|
||||
|
||||
fn schema_id() -> std::borrow::Cow<'static, str> {
|
||||
<$target>::schema_id()
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
<$target>::json_schema(gen)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
use std::num::*;
|
||||
|
||||
macro_rules! nonzero_unsigned_impl {
|
||||
|
@ -12,6 +13,10 @@ macro_rules! nonzero_unsigned_impl {
|
|||
stringify!($type).to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed(stringify!(std::num::$type))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let zero_schema: Schema = SchemaObject {
|
||||
const_value: Some(0.into()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
use std::num::*;
|
||||
|
||||
macro_rules! nonzero_unsigned_impl {
|
||||
|
@ -12,6 +13,10 @@ macro_rules! nonzero_unsigned_impl {
|
|||
stringify!($type).to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed(stringify!(std::num::$type))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut schema: SchemaObject = <$primitive>::json_schema(gen).into();
|
||||
schema.number().minimum = Some(1.0);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
@ -19,6 +20,10 @@ macro_rules! simple_impl {
|
|||
$name.to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed($name)
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::$instance_type.into()),
|
||||
|
@ -64,6 +69,10 @@ macro_rules! unsigned_impl {
|
|||
$format.to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed($format)
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
let mut schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::$instance_type.into()),
|
||||
|
@ -91,6 +100,10 @@ impl JsonSchema for char {
|
|||
"Character".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("char")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::gen::SchemaGenerator;
|
|||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use semver::Version;
|
||||
use std::borrow::Cow;
|
||||
|
||||
impl JsonSchema for Version {
|
||||
no_ref_schema!();
|
||||
|
@ -10,6 +11,10 @@ impl JsonSchema for Version {
|
|||
"Version".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("semver::Version")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
|
||||
macro_rules! seq_impl {
|
||||
($($desc:tt)+) => {
|
||||
|
@ -14,6 +15,11 @@ macro_rules! seq_impl {
|
|||
format!("Array_of_{}", T::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(
|
||||
format!("[{}]", T::schema_id()))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::Array.into()),
|
||||
|
@ -41,6 +47,11 @@ macro_rules! set_impl {
|
|||
format!("Set_of_{}", T::schema_name())
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(
|
||||
format!("Set<{}>", T::schema_id()))
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::Array.into()),
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::gen::SchemaGenerator;
|
|||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use serde_json::{Map, Number, Value};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl JsonSchema for Value {
|
||||
|
@ -11,6 +12,10 @@ impl JsonSchema for Value {
|
|||
"AnyValue".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("AnyValue")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
Schema::Bool(true)
|
||||
}
|
||||
|
@ -25,6 +30,10 @@ impl JsonSchema for Number {
|
|||
"Number".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("Number")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::Number.into()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
impl JsonSchema for Duration {
|
||||
|
@ -8,6 +9,10 @@ impl JsonSchema for Duration {
|
|||
"Duration".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("std::time::Duration")
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
|
@ -29,6 +34,10 @@ impl JsonSchema for SystemTime {
|
|||
"SystemTime".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("std::time::SystemTime")
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
|
||||
macro_rules! tuple_impls {
|
||||
($($len:expr => ($($name:ident)+))+) => {
|
||||
|
@ -14,6 +15,14 @@ macro_rules! tuple_impls {
|
|||
name
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
let mut id = "(".to_owned();
|
||||
id.push_str(&[$($name::schema_id()),+].join(","));
|
||||
id.push(')');
|
||||
|
||||
Cow::Owned(id)
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
let items = vec![
|
||||
$(gen.subschema_for::<$name>()),+
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
use url::Url;
|
||||
|
||||
impl JsonSchema for Url {
|
||||
|
@ -10,6 +11,10 @@ impl JsonSchema for Url {
|
|||
"Url".to_owned()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("url::Url")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
use uuid08::Uuid;
|
||||
|
||||
impl JsonSchema for Uuid {
|
||||
|
@ -10,6 +11,10 @@ impl JsonSchema for Uuid {
|
|||
"Uuid".to_string()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("uuid::Uuid")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::gen::SchemaGenerator;
|
||||
use crate::schema::*;
|
||||
use crate::JsonSchema;
|
||||
use std::borrow::Cow;
|
||||
use uuid1::Uuid;
|
||||
|
||||
impl JsonSchema for Uuid {
|
||||
|
@ -10,6 +11,10 @@ impl JsonSchema for Uuid {
|
|||
"Uuid".to_string()
|
||||
}
|
||||
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Borrowed("uuid::Uuid")
|
||||
}
|
||||
|
||||
fn json_schema(_: &mut SchemaGenerator) -> Schema {
|
||||
SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
|
|
@ -41,6 +41,8 @@ pub mod visit;
|
|||
|
||||
#[cfg(feature = "schemars_derive")]
|
||||
extern crate schemars_derive;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[cfg(feature = "schemars_derive")]
|
||||
pub use schemars_derive::*;
|
||||
|
||||
|
@ -56,7 +58,8 @@ use schema::Schema;
|
|||
///
|
||||
/// This can also be automatically derived on most custom types with `#[derive(JsonSchema)]`.
|
||||
///
|
||||
/// # Example
|
||||
/// # Examples
|
||||
/// Deriving an implementation:
|
||||
/// ```
|
||||
/// use schemars::{schema_for, JsonSchema};
|
||||
///
|
||||
|
@ -67,6 +70,64 @@ use schema::Schema;
|
|||
///
|
||||
/// let my_schema = schema_for!(MyStruct);
|
||||
/// ```
|
||||
///
|
||||
/// When manually implementing `JsonSchema`, as well as determining an appropriate schema,
|
||||
/// you will need to determine an appropriate name and ID for the type.
|
||||
/// For non-generic types, the type name/path are suitable for this:
|
||||
/// ```
|
||||
/// use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
|
||||
/// use std::borrow::Cow;
|
||||
///
|
||||
/// struct NonGenericType;
|
||||
///
|
||||
/// impl JsonSchema for NonGenericType {
|
||||
/// fn schema_name() -> String {
|
||||
/// // Exclude the module path to make the name in generated schemas clearer.
|
||||
/// "NonGenericType".to_owned()
|
||||
/// }
|
||||
///
|
||||
/// fn schema_id() -> Cow<'static, str> {
|
||||
/// // Include the module, in case a type with the same name is in another module/crate
|
||||
/// Cow::Borrowed(concat!(module_path!(), "::NonGenericType"))
|
||||
/// }
|
||||
///
|
||||
/// fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(NonGenericType::schema_id(), <&mut NonGenericType>::schema_id());
|
||||
/// ```
|
||||
///
|
||||
/// But generic type parameters which may affect the generated schema should typically be included in the name/ID:
|
||||
/// ```
|
||||
/// use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
|
||||
/// use std::{borrow::Cow, marker::PhantomData};
|
||||
///
|
||||
/// struct GenericType<T>(PhantomData<T>);
|
||||
///
|
||||
/// impl<T: JsonSchema> JsonSchema for GenericType<T> {
|
||||
/// fn schema_name() -> String {
|
||||
/// format!("GenericType_{}", T::schema_name())
|
||||
/// }
|
||||
///
|
||||
/// fn schema_id() -> Cow<'static, str> {
|
||||
/// Cow::Owned(format!(
|
||||
/// "{}::GenericType<{}>",
|
||||
/// module_path!(),
|
||||
/// T::schema_id()
|
||||
/// ))
|
||||
/// }
|
||||
///
|
||||
/// fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(<GenericType<i32>>::schema_id(), <&mut GenericType<&i32>>::schema_id());
|
||||
/// ```
|
||||
///
|
||||
|
||||
pub trait JsonSchema {
|
||||
/// Whether JSON Schemas generated for this type should be re-used where possible using the `$ref` keyword.
|
||||
///
|
||||
|
@ -83,6 +144,17 @@ pub trait JsonSchema {
|
|||
/// This is used as the title for root schemas, and the key within the root's `definitions` property for subschemas.
|
||||
fn schema_name() -> String;
|
||||
|
||||
/// Returns a string that uniquely identifies the schema produced by this type.
|
||||
///
|
||||
/// This does not have to be a human-readable string, and the value will not itself be included in generated schemas.
|
||||
/// If two types produce different schemas, then they **must** have different `schema_id()`s,
|
||||
/// but two types that produce identical schemas should *ideally* have the same `schema_id()`.
|
||||
///
|
||||
/// The default implementation returns the same value as `schema_name()`.
|
||||
fn schema_id() -> Cow<'static, str> {
|
||||
Cow::Owned(Self::schema_name())
|
||||
}
|
||||
|
||||
/// Generates a JSON Schema for this type.
|
||||
///
|
||||
/// If the returned schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
|
||||
|
|
41
schemars/tests/expected/same_name.json
Normal file
41
schemars/tests/expected/same_name.json
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Config2",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"a_cfg",
|
||||
"b_cfg"
|
||||
],
|
||||
"properties": {
|
||||
"a_cfg": {
|
||||
"$ref": "#/definitions/Config"
|
||||
},
|
||||
"b_cfg": {
|
||||
"$ref": "#/definitions/Config2"
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"Config": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"test"
|
||||
],
|
||||
"properties": {
|
||||
"test": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Config2": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"test2"
|
||||
],
|
||||
"properties": {
|
||||
"test2": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
schemars/tests/same_name.rs
Normal file
35
schemars/tests/same_name.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
mod util;
|
||||
use schemars::JsonSchema;
|
||||
use util::*;
|
||||
|
||||
mod a {
|
||||
use super::*;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(JsonSchema)]
|
||||
pub struct Config {
|
||||
test: String,
|
||||
}
|
||||
}
|
||||
|
||||
mod b {
|
||||
use super::*;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(JsonSchema)]
|
||||
pub struct Config {
|
||||
test2: String,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(JsonSchema)]
|
||||
pub struct Config2 {
|
||||
a_cfg: a::Config,
|
||||
b_cfg: b::Config,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_name() -> TestResult {
|
||||
test_default_generated_schema::<Config2>("same_name")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue