diff --git a/schemars/src/json_schema_impls/indexmap.rs b/schemars/src/json_schema_impls/indexmap.rs index 3bd3e545..3c6caa76 100644 --- a/schemars/src/json_schema_impls/indexmap.rs +++ b/schemars/src/json_schema_impls/indexmap.rs @@ -4,5 +4,5 @@ use crate::JsonSchema; use indexmap::{IndexMap, IndexSet}; use std::collections::{HashMap, HashSet}; -forward_impl!(( JsonSchema for IndexMap) => HashMap); +forward_impl!(( JsonSchema for IndexMap) => HashMap); forward_impl!(( JsonSchema for IndexSet) => HashSet); diff --git a/schemars/src/json_schema_impls/maps.rs b/schemars/src/json_schema_impls/maps.rs index 356464fd..68ce495f 100644 --- a/schemars/src/json_schema_impls/maps.rs +++ b/schemars/src/json_schema_impls/maps.rs @@ -1,11 +1,13 @@ use crate::gen::SchemaGenerator; use crate::schema::*; use crate::JsonSchema; +use crate::{Map, Set}; macro_rules! map_impl { ($($desc:tt)+) => { impl $($desc)+ where + K: JsonSchema, V: JsonSchema, { no_ref_schema!(); @@ -15,11 +17,41 @@ macro_rules! map_impl { } fn json_schema(gen: &mut SchemaGenerator) -> Schema { - let subschema = gen.subschema_for::(); + let k_subschema = gen.subschema_for::(); + let v_subschema = gen.subschema_for::(); + // if the map's key is a reference to another schema, and that schema is an + // enum, our final schema should require that the map has key values + // that are one of the enum values + if let Some(Schema::Object(schema_object)) = gen.dereference(&k_subschema) { + let mut schemas: Vec = vec![]; + if let Some(values) = &schema_object.enum_values { + for value in values { + // enum values all have quotes around them, so remove them + let str_value = &value.to_string(); + let value = format!("{}", &str_value[1..str_value.len()-1]); + + let schema = SchemaObject { + instance_type: Some(InstanceType::Object.into()), + object: Some(Box::new(ObjectValidation { + required: Set::from([value.clone()]), + properties: Map::from([(value, v_subschema.clone())]), + ..Default::default() + })), + ..Default::default() + }; + schemas.push(schema.into()); + } + let mut schema = SchemaObject::default(); + schema.subschemas().one_of = Some(schemas); + return schema.into(); + } + } + // if the key's schema is not a reference, or if the dereferenced key is not an enum, + // we can only enforce map values and not the map keys SchemaObject { instance_type: Some(InstanceType::Object.into()), object: Some(Box::new(ObjectValidation { - additional_properties: Some(Box::new(subschema)), + additional_properties: Some(Box::new(v_subschema)), ..Default::default() })), ..Default::default()