Remove various 'static bounds

Currently using an unreliable hack - assuming type name is unique D:
This commit is contained in:
Graham Esau 2019-08-04 12:30:16 +01:00
parent 4f9956631e
commit 21a29dc6b5
3 changed files with 31 additions and 16 deletions

View file

@ -1,6 +1,5 @@
use crate::make_schema::MakeSchema; use crate::make_schema::{MakeSchema, SchemaTypeId};
use crate::schema::*; use crate::schema::*;
use core::any::TypeId;
use std::collections::BTreeMap as Map; use std::collections::BTreeMap as Map;
use std::collections::BTreeSet as Set; use std::collections::BTreeSet as Set;
use std::iter::FromIterator; use std::iter::FromIterator;
@ -8,7 +7,7 @@ use std::iter::FromIterator;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct SchemaGenerator { pub struct SchemaGenerator {
names: Set<String>, names: Set<String>,
definitions: Map<TypeId, (String, Schema)>, definitions: Map<SchemaTypeId, (String, Schema)>,
} }
impl SchemaGenerator { impl SchemaGenerator {
@ -18,23 +17,24 @@ impl SchemaGenerator {
} }
} }
pub fn subschema_for<T: MakeSchema + 'static>(&mut self) -> Schema { pub fn subschema_for<T: ?Sized + MakeSchema>(&mut self) -> Schema {
if !T::generates_ref_schema() { if !T::generates_ref_schema() {
return T::make_schema(self); return T::make_schema(self);
} }
let type_id = TypeId::of::<T>(); let type_id = T::schema_type_id();
// TODO is there a nicer way to do this? // TODO is there a nicer way to do this?
if !self.definitions.contains_key(&type_id) { if !self.definitions.contains_key(&type_id) {
let name = self.make_unique_name::<T>(); let name = self.make_unique_name::<T>();
self.names.insert(name.clone()); self.names.insert(name.clone());
// insert into definitions BEFORE calling make_schema to avoid infinite recursion // insert into definitions BEFORE calling make_schema to avoid infinite recursion
let dummy = Schema::Bool(false); let dummy = Schema::Bool(false);
self.definitions.insert(type_id, (name.clone(), dummy)); self.definitions
.insert(type_id.clone(), (name.clone(), dummy));
let schema = T::make_schema(self); let schema = T::make_schema(self);
self.definitions self.definitions
.entry(type_id) .entry(type_id.clone())
.and_modify(|(_, s)| *s = schema); .and_modify(|(_, s)| *s = schema);
} }
let ref name = self.definitions.get(&type_id).unwrap().0; let ref name = self.definitions.get(&type_id).unwrap().0;
@ -52,7 +52,7 @@ impl SchemaGenerator {
Map::from_iter(self.definitions.into_iter().map(|(_, v)| v)) Map::from_iter(self.definitions.into_iter().map(|(_, v)| v))
} }
pub fn root_schema_for<T: MakeSchema>(&mut self) -> Schema { pub fn root_schema_for<T: ?Sized + MakeSchema>(&mut self) -> Schema {
let schema = T::make_schema(self); let schema = T::make_schema(self);
if let Schema::Object(mut o) = schema { if let Schema::Object(mut o) = schema {
o.schema = Some("http://json-schema.org/draft-07/schema#".to_owned()); o.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
@ -63,7 +63,7 @@ impl SchemaGenerator {
schema schema
} }
pub fn into_root_schema_for<T: MakeSchema>(mut self) -> Schema { pub fn into_root_schema_for<T: ?Sized + MakeSchema>(mut self) -> Schema {
let schema = T::make_schema(&mut self); let schema = T::make_schema(&mut self);
if let Schema::Object(mut o) = schema { if let Schema::Object(mut o) = schema {
o.schema = Some("http://json-schema.org/draft-07/schema#".to_owned()); o.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
@ -74,7 +74,7 @@ impl SchemaGenerator {
schema schema
} }
fn make_unique_name<T: MakeSchema>(&mut self) -> String { fn make_unique_name<T: ?Sized + MakeSchema>(&mut self) -> String {
let base_name = T::schema_name(); let base_name = T::schema_name();
// TODO remove namespace, remove special chars // TODO remove namespace, remove special chars
if self.names.contains(&base_name) { if self.names.contains(&base_name) {

View file

@ -35,7 +35,8 @@ struct User {
} }
fn main() -> Result<()> { fn main() -> Result<()> {
let schema = <&str>::make_schema(); let gen = generator::SchemaGenerator::new();
let schema = gen.into_root_schema_for::<str>();
let json = serde_json::to_string(&schema)?; let json = serde_json::to_string(&schema)?;
println!("{}", json); println!("{}", json);

View file

@ -3,12 +3,26 @@ use crate::schema::*;
use serde_json::json; use serde_json::json;
use std::collections::BTreeMap as Map; use std::collections::BTreeMap as Map;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct SchemaTypeId(&'static str);
pub trait MakeSchema { pub trait MakeSchema {
fn schema_type_id() -> SchemaTypeId {
// FIXME schema name might not be unique!
SchemaTypeId(core::any::type_name::<Self>())
}
fn schema_name() -> String { fn schema_name() -> String {
// TODO this requires nightly
// It's probably worth removing the default implemenation,
// then make every impl in this file set an explicit name
// Or maybe hide it under feature flag?
core::any::type_name::<Self>().to_owned() core::any::type_name::<Self>().to_owned()
} }
fn generates_ref_schema() -> bool { fn generates_ref_schema() -> bool {
// TODO default this to true as it's safer
// But this would mean every impl in this file needs to override it :(
false false
} }
@ -93,7 +107,7 @@ impl<T> MakeSchema for [T; 0] {
macro_rules! array_impls { macro_rules! array_impls {
($($len:tt)+) => { ($($len:tt)+) => {
$( $(
impl<T: MakeSchema + 'static> MakeSchema for [T; $len] impl<T: MakeSchema> MakeSchema for [T; $len]
{ {
fn make_schema(gen: &mut SchemaGenerator) -> Schema { fn make_schema(gen: &mut SchemaGenerator) -> Schema {
let mut extra_properties = Map::new(); let mut extra_properties = Map::new();
@ -124,7 +138,7 @@ macro_rules! seq_impl {
($($desc:tt)+) => { ($($desc:tt)+) => {
impl $($desc)+ impl $($desc)+
where where
T: MakeSchema + 'static, T: MakeSchema,
{ {
fn make_schema(gen: &mut SchemaGenerator) -> Schema fn make_schema(gen: &mut SchemaGenerator) -> Schema
{ {
@ -152,7 +166,7 @@ macro_rules! map_impl {
impl $($desc)+ impl $($desc)+
where where
K: Into<String>, K: Into<String>,
T: MakeSchema + 'static, T: MakeSchema,
{ {
fn make_schema(gen: &mut SchemaGenerator) -> Schema fn make_schema(gen: &mut SchemaGenerator) -> Schema
{ {
@ -176,7 +190,7 @@ map_impl!(<K, T: Eq + core::hash::Hash, H: core::hash::BuildHasher> MakeSchema f
////////// OPTION ////////// ////////// OPTION //////////
impl<T: MakeSchema + 'static> MakeSchema for Option<T> { impl<T: MakeSchema> MakeSchema for Option<T> {
fn make_schema(gen: &mut SchemaGenerator) -> Schema { fn make_schema(gen: &mut SchemaGenerator) -> Schema {
match gen.subschema_for::<T>() { match gen.subschema_for::<T>() {
Schema::Bool(true) => true.into(), Schema::Bool(true) => true.into(),
@ -196,7 +210,7 @@ macro_rules! deref_impl {
($($desc:tt)+) => { ($($desc:tt)+) => {
impl $($desc)+ impl $($desc)+
where where
T: MakeSchema + 'static, T: MakeSchema,
{ {
fn make_schema(gen: &mut SchemaGenerator) -> Schema { fn make_schema(gen: &mut SchemaGenerator) -> Schema {
gen.subschema_for::<T>() gen.subschema_for::<T>()