| use crate::schema::*; |
| use crate::JsonSchema; |
| use crate::{gen::SchemaGenerator, Map}; |
| use serde_json::{Error, Value}; |
| use std::{convert::TryInto, fmt::Display}; |
| |
| pub(crate) struct Serializer<'a> { |
| pub(crate) gen: &'a mut SchemaGenerator, |
| pub(crate) include_title: bool, |
| } |
| |
| pub(crate) struct SerializeSeq<'a> { |
| gen: &'a mut SchemaGenerator, |
| items: Option<Schema>, |
| } |
| |
| pub(crate) struct SerializeTuple<'a> { |
| gen: &'a mut SchemaGenerator, |
| items: Vec<Schema>, |
| title: &'static str, |
| } |
| |
| pub(crate) struct SerializeMap<'a> { |
| gen: &'a mut SchemaGenerator, |
| properties: Map<String, Schema>, |
| current_key: Option<String>, |
| title: &'static str, |
| } |
| |
| macro_rules! forward_to_subschema_for { |
| ($fn:ident, $ty:ty) => { |
| fn $fn(self, _value: $ty) -> Result<Self::Ok, Self::Error> { |
| Ok(self.gen.subschema_for::<$ty>()) |
| } |
| }; |
| } |
| |
| macro_rules! return_instance_type { |
| ($fn:ident, $ty:ty, $instance_type:ident) => { |
| fn $fn(self, _value: $ty) -> Result<Self::Ok, Self::Error> { |
| Ok(SchemaObject { |
| instance_type: Some(InstanceType::$instance_type.into()), |
| ..Default::default() |
| } |
| .into()) |
| } |
| }; |
| } |
| |
| impl<'a> serde::Serializer for Serializer<'a> { |
| type Ok = Schema; |
| type Error = Error; |
| |
| type SerializeSeq = SerializeSeq<'a>; |
| type SerializeTuple = SerializeTuple<'a>; |
| type SerializeTupleStruct = SerializeTuple<'a>; |
| type SerializeTupleVariant = Self; |
| type SerializeMap = SerializeMap<'a>; |
| type SerializeStruct = SerializeMap<'a>; |
| type SerializeStructVariant = Self; |
| |
| return_instance_type!(serialize_i8, i8, Integer); |
| return_instance_type!(serialize_i16, i16, Integer); |
| return_instance_type!(serialize_i32, i32, Integer); |
| return_instance_type!(serialize_i64, i64, Integer); |
| return_instance_type!(serialize_i128, i128, Integer); |
| return_instance_type!(serialize_u8, u8, Integer); |
| return_instance_type!(serialize_u16, u16, Integer); |
| return_instance_type!(serialize_u32, u32, Integer); |
| return_instance_type!(serialize_u64, u64, Integer); |
| return_instance_type!(serialize_u128, u128, Integer); |
| return_instance_type!(serialize_f32, f32, Number); |
| return_instance_type!(serialize_f64, f64, Number); |
| |
| forward_to_subschema_for!(serialize_bool, bool); |
| forward_to_subschema_for!(serialize_char, char); |
| forward_to_subschema_for!(serialize_str, &str); |
| forward_to_subschema_for!(serialize_bytes, &[u8]); |
| |
| fn collect_str<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error> |
| where |
| T: Display, |
| { |
| Ok(self.gen.subschema_for::<&str>()) |
| } |
| |
| fn collect_map<K, V, I>(self, iter: I) -> Result<Self::Ok, Self::Error> |
| where |
| K: serde::Serialize, |
| V: serde::Serialize, |
| I: IntoIterator<Item = (K, V)>, |
| { |
| let value_schema = iter |
| .into_iter() |
| .try_fold(None, |acc, (_, v)| { |
| if acc == Some(Schema::Bool(true)) { |
| return Ok(acc); |
| } |
| |
| let schema = v.serialize(Serializer { |
| gen: self.gen, |
| include_title: false, |
| })?; |
| Ok(match &acc { |
| None => Some(schema), |
| Some(items) if items != &schema => Some(Schema::Bool(true)), |
| _ => acc, |
| }) |
| })? |
| .unwrap_or(Schema::Bool(true)); |
| |
| Ok(SchemaObject { |
| instance_type: Some(InstanceType::Object.into()), |
| object: Some(Box::new(ObjectValidation { |
| additional_properties: Some(Box::new(value_schema)), |
| ..ObjectValidation::default() |
| })), |
| ..SchemaObject::default() |
| } |
| .into()) |
| } |
| |
| fn serialize_none(self) -> Result<Self::Ok, Self::Error> { |
| Ok(self.gen.subschema_for::<Option<Value>>()) |
| } |
| |
| fn serialize_unit(self) -> Result<Self::Ok, Self::Error> { |
| self.serialize_none() |
| } |
| |
| fn serialize_some<T: ?Sized>(mut self, value: &T) -> Result<Self::Ok, Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| // FIXME nasty duplication of `impl JsonSchema for Option<T>` |
| fn add_null_type(instance_type: &mut SingleOrVec<InstanceType>) { |
| match instance_type { |
| SingleOrVec::Single(ty) if **ty != InstanceType::Null => { |
| *instance_type = vec![**ty, InstanceType::Null].into() |
| } |
| SingleOrVec::Vec(ty) if !ty.contains(&InstanceType::Null) => { |
| ty.push(InstanceType::Null) |
| } |
| _ => {} |
| }; |
| } |
| |
| let mut schema = value.serialize(Serializer { |
| gen: self.gen, |
| include_title: false, |
| })?; |
| |
| if self.gen.settings().option_add_null_type { |
| schema = match schema { |
| Schema::Bool(true) => Schema::Bool(true), |
| Schema::Bool(false) => <()>::json_schema(&mut self.gen), |
| Schema::Object(SchemaObject { |
| instance_type: Some(ref mut instance_type), |
| .. |
| }) => { |
| add_null_type(instance_type); |
| schema |
| } |
| schema => SchemaObject { |
| subschemas: Some(Box::new(SubschemaValidation { |
| any_of: Some(vec![schema, <()>::json_schema(&mut self.gen)]), |
| ..Default::default() |
| })), |
| ..Default::default() |
| } |
| .into(), |
| } |
| } |
| |
| if self.gen.settings().option_nullable { |
| let mut schema_obj = schema.into_object(); |
| schema_obj |
| .extensions |
| .insert("nullable".to_owned(), serde_json::json!(true)); |
| schema = Schema::Object(schema_obj); |
| }; |
| |
| Ok(schema) |
| } |
| |
| fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> { |
| Ok(self.gen.subschema_for::<()>()) |
| } |
| |
| fn serialize_unit_variant( |
| self, |
| _name: &'static str, |
| _variant_index: u32, |
| _variant: &'static str, |
| ) -> Result<Self::Ok, Self::Error> { |
| Ok(Schema::Bool(true)) |
| } |
| |
| fn serialize_newtype_struct<T: ?Sized>( |
| self, |
| name: &'static str, |
| value: &T, |
| ) -> Result<Self::Ok, Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| let include_title = self.include_title; |
| let mut result = value.serialize(self); |
| |
| if include_title { |
| if let Ok(Schema::Object(ref mut object)) = result { |
| object.metadata().title = Some(name.to_string()); |
| } |
| } |
| |
| result |
| } |
| |
| fn serialize_newtype_variant<T: ?Sized>( |
| self, |
| _name: &'static str, |
| _variant_index: u32, |
| _variant: &'static str, |
| _value: &T, |
| ) -> Result<Self::Ok, Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| Ok(Schema::Bool(true)) |
| } |
| |
| fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> { |
| Ok(SerializeSeq { |
| gen: self.gen, |
| items: None, |
| }) |
| } |
| |
| fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> { |
| Ok(SerializeTuple { |
| gen: self.gen, |
| items: Vec::with_capacity(len), |
| title: "", |
| }) |
| } |
| |
| fn serialize_tuple_struct( |
| self, |
| name: &'static str, |
| len: usize, |
| ) -> Result<Self::SerializeTupleStruct, Self::Error> { |
| let title = if self.include_title { name } else { "" }; |
| Ok(SerializeTuple { |
| gen: self.gen, |
| items: Vec::with_capacity(len), |
| title, |
| }) |
| } |
| |
| fn serialize_tuple_variant( |
| self, |
| _name: &'static str, |
| _variant_index: u32, |
| _variant: &'static str, |
| _len: usize, |
| ) -> Result<Self::SerializeTupleVariant, Self::Error> { |
| Ok(self) |
| } |
| |
| fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> { |
| Ok(SerializeMap { |
| gen: self.gen, |
| properties: Map::new(), |
| current_key: None, |
| title: "", |
| }) |
| } |
| |
| fn serialize_struct( |
| self, |
| name: &'static str, |
| _len: usize, |
| ) -> Result<Self::SerializeStruct, Self::Error> { |
| let title = if self.include_title { name } else { "" }; |
| Ok(SerializeMap { |
| gen: self.gen, |
| properties: Map::new(), |
| current_key: None, |
| title, |
| }) |
| } |
| |
| fn serialize_struct_variant( |
| self, |
| _name: &'static str, |
| _variant_index: u32, |
| _variant: &'static str, |
| _len: usize, |
| ) -> Result<Self::SerializeStructVariant, Self::Error> { |
| Ok(self) |
| } |
| } |
| |
| impl serde::ser::SerializeTupleVariant for Serializer<'_> { |
| type Ok = Schema; |
| type Error = Error; |
| |
| fn serialize_field<T: ?Sized>(&mut self, _value: &T) -> Result<(), Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| Ok(()) |
| } |
| |
| fn end(self) -> Result<Self::Ok, Self::Error> { |
| Ok(Schema::Bool(true)) |
| } |
| } |
| |
| impl serde::ser::SerializeStructVariant for Serializer<'_> { |
| type Ok = Schema; |
| type Error = Error; |
| |
| fn serialize_field<T: ?Sized>( |
| &mut self, |
| _key: &'static str, |
| _value: &T, |
| ) -> Result<(), Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| Ok(()) |
| } |
| |
| fn end(self) -> Result<Self::Ok, Self::Error> { |
| Ok(Schema::Bool(true)) |
| } |
| } |
| |
| impl serde::ser::SerializeSeq for SerializeSeq<'_> { |
| type Ok = Schema; |
| type Error = Error; |
| |
| fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| if self.items != Some(Schema::Bool(true)) { |
| let schema = value.serialize(Serializer { |
| gen: self.gen, |
| include_title: false, |
| })?; |
| match &self.items { |
| None => self.items = Some(schema), |
| Some(items) => { |
| if items != &schema { |
| self.items = Some(Schema::Bool(true)) |
| } |
| } |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| fn end(self) -> Result<Self::Ok, Self::Error> { |
| let items = self.items.unwrap_or(Schema::Bool(true)); |
| Ok(SchemaObject { |
| instance_type: Some(InstanceType::Array.into()), |
| array: Some(Box::new(ArrayValidation { |
| items: Some(items.into()), |
| ..ArrayValidation::default() |
| })), |
| ..SchemaObject::default() |
| } |
| .into()) |
| } |
| } |
| |
| impl serde::ser::SerializeTuple for SerializeTuple<'_> { |
| type Ok = Schema; |
| type Error = Error; |
| |
| fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| let schema = value.serialize(Serializer { |
| gen: self.gen, |
| include_title: false, |
| })?; |
| self.items.push(schema); |
| Ok(()) |
| } |
| |
| fn end(self) -> Result<Self::Ok, Self::Error> { |
| let len = self.items.len().try_into().ok(); |
| let mut schema = SchemaObject { |
| instance_type: Some(InstanceType::Array.into()), |
| array: Some(Box::new(ArrayValidation { |
| items: Some(SingleOrVec::Vec(self.items)), |
| max_items: len, |
| min_items: len, |
| ..ArrayValidation::default() |
| })), |
| ..SchemaObject::default() |
| }; |
| |
| if !self.title.is_empty() { |
| schema.metadata().title = Some(self.title.to_owned()); |
| } |
| |
| Ok(schema.into()) |
| } |
| } |
| |
| impl serde::ser::SerializeTupleStruct for SerializeTuple<'_> { |
| type Ok = Schema; |
| type Error = Error; |
| |
| fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| serde::ser::SerializeTuple::serialize_element(self, value) |
| } |
| |
| fn end(self) -> Result<Self::Ok, Self::Error> { |
| serde::ser::SerializeTuple::end(self) |
| } |
| } |
| |
| impl serde::ser::SerializeMap for SerializeMap<'_> { |
| type Ok = Schema; |
| type Error = Error; |
| |
| fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| // FIXME this is too lenient - we should return an error if serde_json |
| // doesn't allow T to be a key of a map. |
| let json = serde_json::to_string(key)?; |
| self.current_key = Some( |
| json.trim_start_matches('"') |
| .trim_end_matches('"') |
| .to_string(), |
| ); |
| |
| Ok(()) |
| } |
| |
| fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| let key = self.current_key.take().unwrap_or_default(); |
| let schema = value.serialize(Serializer { |
| gen: self.gen, |
| include_title: false, |
| })?; |
| self.properties.insert(key, schema); |
| |
| Ok(()) |
| } |
| |
| fn end(self) -> Result<Self::Ok, Self::Error> { |
| let mut schema = SchemaObject { |
| instance_type: Some(InstanceType::Object.into()), |
| object: Some(Box::new(ObjectValidation { |
| properties: self.properties, |
| ..ObjectValidation::default() |
| })), |
| ..SchemaObject::default() |
| }; |
| |
| if !self.title.is_empty() { |
| schema.metadata().title = Some(self.title.to_owned()); |
| } |
| |
| Ok(schema.into()) |
| } |
| } |
| |
| impl serde::ser::SerializeStruct for SerializeMap<'_> { |
| type Ok = Schema; |
| type Error = Error; |
| |
| fn serialize_field<T: ?Sized>( |
| &mut self, |
| key: &'static str, |
| value: &T, |
| ) -> Result<(), Self::Error> |
| where |
| T: serde::Serialize, |
| { |
| let prop_schema = value.serialize(Serializer { |
| gen: self.gen, |
| include_title: false, |
| })?; |
| self.properties.insert(key.to_string(), prop_schema); |
| |
| Ok(()) |
| } |
| |
| fn end(self) -> Result<Self::Ok, Self::Error> { |
| serde::ser::SerializeMap::end(self) |
| } |
| } |