Add Schema methods insert, get and remove

These are just convenience methods that delegate to the inner object. `insert` will also convert bool schemas to object schemas.
This commit is contained in:
Graham Esau 2024-08-30 11:40:29 +01:00
parent d6c8b6b022
commit 0672c862c8
7 changed files with 109 additions and 76 deletions

View file

@ -129,9 +129,7 @@ pub struct MyTransform;
impl Transform for MyTransform {
fn transform(&mut self, schema: &mut Schema) {
// First, make our change to this schema
if let Some(obj) = schema.as_object_mut() {
obj.insert("my_property".to_string(), serde_json::json!("hello world"));
}
schema.insert("my_property".to_string(), serde_json::json!("hello world"));
// Then apply the transform to any subschemas
transform_subschemas(self, schema);
@ -147,9 +145,7 @@ Also, since `Transform` is now implemented for functions that take a single `&mu
```rust
fn my_transform(schema: &mut Schema) {
// First, make our change to this schema
if let Some(obj) = schema.as_object_mut() {
obj.insert("my_property".to_string(), serde_json::json!("hello world"));
}
schema.insert("my_property".to_string(), serde_json::json!("hello world"));
// Then apply the transform to any subschemas
transform_subschemas(&mut my_transform, schema);
@ -165,9 +161,7 @@ Finally, you can also use the `RecursiveTransform` newtype to convert a non-recu
```rust
fn my_transform2(schema: &mut Schema) {
if let Some(obj) = schema.as_object_mut() {
obj.insert("my_property".to_string(), serde_json::json!("hello world"));
}
schema.insert("my_property".to_string(), serde_json::json!("hello world"));
}
let mut schema = schemars::schema_for!(str);

View file

@ -25,9 +25,7 @@ pub enum MyEnum {
}
fn remove_format(schema: &mut Schema) {
if let Some(obj) = schema.as_object_mut() {
obj.remove("format");
}
schema.remove("format");
}
fn main() {

View file

@ -25,9 +25,7 @@ pub enum MyEnum {
}
fn remove_format(schema: &mut Schema) {
if let Some(obj) = schema.as_object_mut() {
obj.remove("format");
}
schema.remove("format");
}
fn main() {

View file

@ -20,9 +20,7 @@ pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
let mut schema = T::_schemars_private_non_optional_json_schema(generator);
if T::_schemars_private_is_option() && !required {
if let Some(object) = schema.as_object_mut() {
object.remove("required");
}
schema.remove("required");
}
// Always allow aditional/unevaluated properties, because the outer struct determines
@ -33,16 +31,14 @@ pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
}
fn allow_unknown_properties(schema: &mut Schema) {
if let Some(obj) = schema.as_object_mut() {
if obj.get("additionalProperties").and_then(Value::as_bool) == Some(false) {
obj.remove("additionalProperties");
if schema.get("additionalProperties").and_then(Value::as_bool) == Some(false) {
schema.remove("additionalProperties");
}
if obj.get("unevaluatedProperties").and_then(Value::as_bool) == Some(false) {
obj.remove("unevaluatedProperties");
if schema.get("unevaluatedProperties").and_then(Value::as_bool) == Some(false) {
schema.remove("unevaluatedProperties");
}
transform_immediate_subschemas(&mut allow_unknown_properties, schema);
}
}
/// Hack to simulate specialization:
@ -211,7 +207,7 @@ pub fn apply_inner_validation(schema: &mut Schema, f: fn(&mut Schema) -> ()) {
if let Some(inner_schema) = schema
.as_object_mut()
.and_then(|o| o.get_mut("items"))
.and_then(|i| <&mut Schema>::try_from(i).ok())
.and_then(|i| i.try_into().ok())
{
f(inner_schema);
}

View file

@ -138,10 +138,80 @@ impl Schema {
self.0 = Value::Object(map);
}
self.as_object_mut()
self.0
.as_object_mut()
.expect("Schema value should be of type Object.")
}
/// Inserts a property into the schema, replacing any previous value.
///
/// If the schema wraps a bool value, it will first be converted into an equivalent object schema.
///
/// If the schema did not have this key present, `None` is returned.
///
/// If the schema did have this key present, the value is updated, and the old value is returned.
///
/// # Example
/// ```
/// use schemars::json_schema;
/// use serde_json::json;
///
/// let mut schema = json_schema!(true);
/// assert_eq!(schema.insert("type".to_owned(), "array".into()), None);
/// assert_eq!(schema.insert("type".to_owned(), "object".into()), Some(json!("array")));
///
/// assert_eq!(schema, json_schema!({"type": "object"}));
/// ```
pub fn insert(&mut self, k: String, v: Value) -> Option<Value> {
self.ensure_object().insert(k, v)
}
/// If the `Schema`'s underlying JSON value is an object, gets a reference to that object's value for the given key.
///
/// This always returns `None` for bool schemas.
///
/// # Example
/// ```
/// use schemars::json_schema;
/// use serde_json::json;
///
/// let obj_schema = json_schema!({"type": "array"});
/// assert_eq!(obj_schema.get("type"), Some(&json!("array")));
/// assert_eq!(obj_schema.get("format"), None);
///
/// let bool_schema = json_schema!(true);
/// assert_eq!(bool_schema.get("type"), None);
/// ```
pub fn get<Q>(&self, key: &Q) -> Option<&Value>
where
String: core::borrow::Borrow<Q>,
Q: ?Sized + Ord + Eq + core::hash::Hash,
{
self.0.as_object().and_then(|o| o.get(key))
}
/// If the `Schema`'s underlying JSON value is an object, removes and returns its value for the given key.
///
/// This always returns `None` for bool schemas, without modifying them.
///
/// # Example
/// ```
/// use schemars::json_schema;
/// use serde_json::json;
///
/// let mut schema = json_schema!({"type": "array"});
/// assert_eq!(schema.remove("type"), Some(json!("array")));
/// assert_eq!(schema, json_schema!({}));
///
/// ```
pub fn remove<Q>(&mut self, key: &Q) -> Option<Value>
where
String: core::borrow::Borrow<Q>,
Q: ?Sized + Ord + Eq + core::hash::Hash,
{
self.0.as_object_mut().and_then(|o| o.remove(key))
}
pub(crate) fn has_type(&self, ty: &str) -> bool {
match self.0.get("type") {
Some(Value::Array(values)) => values.iter().any(|v| v.as_str() == Some(ty)),

View file

@ -21,9 +21,7 @@ pub struct MyTransform;
impl Transform for MyTransform {
fn transform(&mut self, schema: &mut Schema) {
// First, make our change to this schema
if let Some(obj) = schema.as_object_mut() {
obj.insert("my_property".to_string(), serde_json::json!("hello world"));
}
schema.insert("my_property".to_string(), "hello world".into());
// Then apply the transform to any subschemas
transform_subschemas(self, schema);
@ -55,9 +53,7 @@ The same example with a `fn` transform:
use schemars::transform::transform_subschemas;
fn add_property(schema: &mut Schema) {
if let Some(obj) = schema.as_object_mut() {
obj.insert("my_property".to_string(), serde_json::json!("hello world"));
}
schema.insert("my_property".to_string(), "hello world".into());
transform_subschemas(&mut add_property, schema)
}
@ -87,9 +83,7 @@ And the same example using a closure wrapped in a `RecursiveTransform`:
use schemars::transform::{Transform, RecursiveTransform};
let mut transform = RecursiveTransform(|schema: &mut Schema| {
if let Some(obj) = schema.as_object_mut() {
obj.insert("my_property".to_string(), serde_json::json!("hello world"));
}
schema.insert("my_property".to_string(), "hello world".into());
});
let mut schema = json_schema!({
@ -236,9 +230,7 @@ pub(crate) fn transform_immediate_subschemas<T: Transform + ?Sized>(
/// use schemars::transform::{Transform, RecursiveTransform};
///
/// let mut transform = RecursiveTransform(|schema: &mut Schema| {
/// if let Some(obj) = schema.as_object_mut() {
/// obj.insert("my_property".to_string(), serde_json::json!("hello world"));
/// }
/// schema.insert("my_property".to_string(), "hello world".into());
/// });
///
/// let mut schema = json_schema!({
@ -289,9 +281,7 @@ impl Transform for ReplaceBoolSchemas {
if let Some((ap_key, ap_value)) = obj.remove_entry("additionalProperties") {
transform_subschemas(self, schema);
if let Some(obj) = schema.as_object_mut() {
obj.insert(ap_key, ap_value);
}
schema.insert(ap_key, ap_value);
return;
}
@ -339,11 +329,9 @@ impl Transform for SetSingleExample {
fn transform(&mut self, schema: &mut Schema) {
transform_subschemas(self, schema);
if let Some(obj) = schema.as_object_mut() {
if let Some(Value::Array(examples)) = obj.remove("examples") {
if let Some(Value::Array(examples)) = schema.remove("examples") {
if let Some(first_example) = examples.into_iter().next() {
obj.insert("example".into(), first_example);
}
schema.insert("example".into(), first_example);
}
}
}
@ -360,10 +348,8 @@ impl Transform for ReplaceConstValue {
fn transform(&mut self, schema: &mut Schema) {
transform_subschemas(self, schema);
if let Some(obj) = schema.as_object_mut() {
if let Some(value) = obj.remove("const") {
obj.insert("enum".into(), Value::Array(vec![value]));
}
if let Some(value) = schema.remove("const") {
schema.insert("enum".into(), Value::Array(vec![value]));
}
}
}
@ -381,13 +367,11 @@ impl Transform for ReplacePrefixItems {
fn transform(&mut self, schema: &mut Schema) {
transform_subschemas(self, schema);
if let Some(obj) = schema.as_object_mut() {
if let Some(prefix_items) = obj.remove("prefixItems") {
let previous_items = obj.insert("items".to_owned(), prefix_items);
if let Some(prefix_items) = schema.remove("prefixItems") {
let previous_items = schema.insert("items".to_owned(), prefix_items);
if let Some(previous_items) = previous_items {
obj.insert("additionalItems".to_owned(), previous_items);
}
schema.insert("additionalItems".to_owned(), previous_items);
}
}
}
@ -400,14 +384,11 @@ impl Transform for ReplaceUnevaluatedProperties {
fn transform(&mut self, schema: &mut Schema) {
transform_subschemas(self, schema);
let Some(obj) = schema.as_object_mut() else {
return;
};
let Some(up) = obj.remove("unevaluatedProperties") else {
let Some(up) = schema.remove("unevaluatedProperties") else {
return;
};
obj.insert("additionalProperties".to_owned(), up);
schema.insert("additionalProperties".to_owned(), up);
let mut gather_property_names = GatherPropertyNames::default();
gather_property_names.transform(schema);

View file

@ -1,24 +1,20 @@
mod util;
use schemars::{transform::RecursiveTransform, JsonSchema, Schema};
use serde_json::Value;
use serde_json::{Map, Value};
use util::*;
fn capitalize_type(schema: &mut Schema) {
if let Some(obj) = schema.as_object_mut() {
if let Some(Value::String(ty)) = obj.get("type") {
obj.insert("upperType".to_owned(), ty.to_uppercase().into());
}
if let Some(Value::String(ty)) = schema.get("type") {
schema.insert("upperType".to_owned(), ty.to_uppercase().into());
}
}
fn insert_property_count(schema: &mut Schema) {
if let Some(obj) = schema.as_object_mut() {
let count = obj
let count = schema
.get("properties")
.and_then(|p| p.as_object())
.map_or(0, |p| p.len());
obj.insert("propertyCount".to_owned(), count.into());
}
.and_then(Value::as_object)
.map_or(0, Map::len);
schema.insert("propertyCount".to_owned(), count.into());
}
#[allow(dead_code)]