use crate::error::SchemaError;
use crate::graph::schema::GraphSchema;
use crate::graph::schema::parse::MAX_SCHEMA_DEPTH;
use crate::value::physical::{lift_untyped, read_json};
use crate::value::{ElementProperty, PropertyMap};
#[derive(Debug, Clone, PartialEq)]
pub struct SchemaPatch {
document: ElementProperty,
}
impl SchemaPatch {
pub fn from_json(text: &str) -> Result<SchemaPatch, SchemaError> {
let value =
read_json(text, MAX_SCHEMA_DEPTH).map_err(|err| SchemaError::InvalidSchema {
path: "/".to_string(),
reason: format!("the patch is not valid JSON: {err}"),
hint: String::new(),
})?;
Ok(SchemaPatch {
document: lift_untyped(value),
})
}
pub fn from_value(value: ElementProperty) -> SchemaPatch {
SchemaPatch { document: value }
}
pub fn document(&self) -> &ElementProperty {
&self.document
}
pub fn apply(&self, schema: Option<&GraphSchema>) -> Result<GraphSchema, SchemaError> {
let target = match schema {
Some(schema) => schema.to_value(),
None => ElementProperty::Object(Box::default()),
};
GraphSchema::from_value(&merge_patch(target, &self.document))
}
}
pub(crate) fn merge_patch(target: ElementProperty, patch: &ElementProperty) -> ElementProperty {
let ElementProperty::Object(patch_members) = patch else {
return patch.clone();
};
let mut target_members: PropertyMap = match target {
ElementProperty::Object(members) => *members,
_ => PropertyMap::default(),
};
for (key, value) in patch_members.iter() {
if matches!(value, ElementProperty::Null) {
target_members.shift_remove(key);
} else if let Some(slot) = target_members.get_mut(key) {
let current = std::mem::replace(slot, ElementProperty::Null);
*slot = merge_patch(current, value);
} else {
target_members.insert(key.clone(), merge_patch(ElementProperty::Null, value));
}
}
ElementProperty::Object(Box::new(target_members))
}