use super::{
EntityUidJSON, JSONValue, JsonDeserializationError, JsonDeserializationErrorContext,
JsonSerializationError, SchemaType, TypeAndId, ValueParser,
};
use crate::ast::{Entity, EntityType, RestrictedExpr};
use crate::entities::{Entities, EntitiesError, TCComputation};
use crate::extensions::Extensions;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct EntityJSON {
uid: EntityUidJSON,
attrs: HashMap<SmolStr, serde_json::Value>,
parents: Vec<EntityUidJSON>,
}
pub trait Schema {
fn attr_type(&self, entity_type: &EntityType, attr: &str) -> Option<SchemaType>;
fn required_attrs<'s>(
&'s self,
entity_type: &EntityType,
) -> Box<dyn Iterator<Item = SmolStr> + 's>;
}
#[derive(Debug, Clone)]
pub struct NullSchema;
impl Schema for NullSchema {
fn attr_type(&self, _entity_type: &EntityType, _attr: &str) -> Option<SchemaType> {
None
}
fn required_attrs(&self, _entity_type: &EntityType) -> Box<dyn Iterator<Item = SmolStr>> {
Box::new(std::iter::empty())
}
}
#[derive(Debug, Clone)]
pub struct EntityJsonParser<'e, 's, S: Schema = NullSchema> {
schema: Option<&'s S>,
extensions: Extensions<'e>,
tc_computation: TCComputation,
}
impl<'e, 's, S: Schema> EntityJsonParser<'e, 's, S> {
pub fn new(
schema: Option<&'s S>,
extensions: Extensions<'e>,
tc_computation: TCComputation,
) -> Self {
Self {
schema,
extensions,
tc_computation,
}
}
pub fn from_json_str(&self, json: &str) -> Result<Entities, EntitiesError> {
let ejsons: Vec<EntityJSON> =
serde_json::from_str(json).map_err(JsonDeserializationError::from)?;
self.parse_ejsons(ejsons)
}
pub fn from_json_value(&self, json: serde_json::Value) -> Result<Entities, EntitiesError> {
let ejsons: Vec<EntityJSON> =
serde_json::from_value(json).map_err(JsonDeserializationError::from)?;
self.parse_ejsons(ejsons)
}
pub fn from_json_file(&self, json: impl std::io::Read) -> Result<Entities, EntitiesError> {
let ejsons: Vec<EntityJSON> =
serde_json::from_reader(json).map_err(JsonDeserializationError::from)?;
self.parse_ejsons(ejsons)
}
fn parse_ejsons(
&self,
ejsons: impl IntoIterator<Item = EntityJSON>,
) -> Result<Entities, EntitiesError> {
let entities = ejsons
.into_iter()
.map(|ejson| self.parse_ejson(ejson))
.collect::<Result<Vec<Entity>, _>>()?;
Entities::from_entities(entities, self.tc_computation)
}
fn parse_ejson(&self, ejson: EntityJSON) -> Result<Entity, JsonDeserializationError> {
let uid = ejson
.uid
.into_euid(|| JsonDeserializationErrorContext::EntityUid)?;
let etype = uid.entity_type();
match self.schema {
None => {}
Some(schema) => {
for required_attr in schema.required_attrs(etype) {
if ejson.attrs.contains_key(&required_attr) {
} else {
return Err(JsonDeserializationError::MissingRequiredEntityAttr {
uid,
attr: required_attr,
});
}
}
}
}
let vparser = ValueParser::new(self.extensions.clone());
let attrs: HashMap<SmolStr, RestrictedExpr> = ejson
.attrs
.into_iter()
.map(|(k, v)| match self.schema {
None => Ok((
k.clone(),
vparser.val_into_rexpr(v, None, || {
JsonDeserializationErrorContext::EntityAttribute {
uid: uid.clone(),
attr: k.clone(),
}
})?,
)),
Some(schema) => {
let (rexpr, expected_ty) = match schema.attr_type(etype, &k) {
None => {
return Err(JsonDeserializationError::UnexpectedEntityAttr {
uid: uid.clone(),
attr: k,
})
}
Some(expected_ty) => (
vparser.val_into_rexpr(v, Some(&expected_ty), || {
JsonDeserializationErrorContext::EntityAttribute {
uid: uid.clone(),
attr: k.clone(),
}
})?,
expected_ty,
),
};
let actual_ty = vparser.type_of_rexpr(rexpr.as_borrowed(), || {
JsonDeserializationErrorContext::EntityAttribute {
uid: uid.clone(),
attr: k.clone(),
}
})?;
if actual_ty.is_consistent_with(&expected_ty) {
Ok((k, rexpr))
} else {
Err(JsonDeserializationError::TypeMismatch {
ctx: JsonDeserializationErrorContext::EntityAttribute {
uid: uid.clone(),
attr: k,
},
expected: Box::new(expected_ty),
actual: Box::new(actual_ty),
})
}
}
})
.collect::<Result<_, JsonDeserializationError>>()?;
let parents = ejson
.parents
.into_iter()
.map(|parent| {
parent.into_euid(|| JsonDeserializationErrorContext::EntityParents {
uid: uid.clone(),
})
})
.collect::<Result<_, JsonDeserializationError>>()?;
Ok(Entity::new(uid, attrs, parents))
}
}
impl EntityJSON {
pub fn from_entity(entity: &Entity) -> Result<Self, JsonSerializationError> {
Ok(Self {
uid: EntityUidJSON::ImplicitEntityEscape(TypeAndId::from(entity.uid())),
attrs: entity
.attrs()
.iter()
.map(|(k, expr)| {
Ok((
k.clone(),
serde_json::to_value(JSONValue::from_expr(expr.as_borrowed())?)?,
))
})
.collect::<Result<_, JsonSerializationError>>()?,
parents: entity
.ancestors()
.map(|euid| EntityUidJSON::ImplicitEntityEscape(TypeAndId::from(euid.clone())))
.collect(),
})
}
}