use crate::ro_crate::constraints::EntityValue;
use crate::ro_crate::constraints::{DataType, Id};
use crate::ro_crate::modify::*;
use serde::ser::SerializeMap;
use serde::{
de::{self, MapAccess, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone)]
pub struct MetadataDescriptor {
pub id: String,
pub type_: DataType,
pub conforms_to: Id,
pub about: Id,
pub dynamic_entity: Option<HashMap<String, EntityValue>>,
}
impl fmt::Display for MetadataDescriptor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Metadata Description: ID={}, Type={:?}, conforms_to={:?}, about={:?}",
self.id, self.type_, self.conforms_to, self.about
)
}
}
impl DynamicEntityManipulation for MetadataDescriptor {
fn dynamic_entity(&mut self) -> &mut Option<HashMap<String, EntityValue>> {
&mut self.dynamic_entity
}
fn dynamic_entity_immut(&self) -> &Option<HashMap<String, EntityValue>> {
&self.dynamic_entity
}
}
impl MetadataDescriptor {
pub fn get_property_value(&self, property: &str) -> Option<(String, EntityValue)> {
match property {
"@type" => Some((
self.id.clone(),
EntityValue::EntityDataType(self.type_.clone()),
)),
"conformsTo" => Some((
self.id.clone(),
EntityValue::EntityId(self.conforms_to.clone()),
)),
"about" => Some((self.id.clone(), EntityValue::EntityId(self.about.clone()))),
_ => self
.search_properties_for_value(property)
.map(|value| (self.id.clone(), value)),
}
}
pub fn find_value_details(&self, target_value: &EntityValue) -> Option<(String, String)> {
if let Some(dynamic_entity) = &self.dynamic_entity {
if let Some(key) = search_dynamic_entity_for_key(dynamic_entity, target_value) {
return Some((self.id.clone(), key));
}
}
None
}
pub fn get_linked_ids(&self) -> Vec<Id> {
let mut ids = Vec::new();
ids.push(self.about.clone());
ids.push(self.conforms_to.clone());
if let Some(dynamic_entity) = &self.dynamic_entity {
for value in dynamic_entity.values() {
Self::extract_ids_from_entity_value(value, &mut ids);
}
}
ids
}
fn extract_ids_from_entity_value(value: &EntityValue, ids: &mut Vec<Id>) {
match value {
EntityValue::EntityId(id) => {
ids.push(id.clone());
}
EntityValue::EntityVec(vec) => {
for v in vec {
Self::extract_ids_from_entity_value(v, ids);
}
}
EntityValue::EntityObject(map) => {
for v in map.values() {
Self::extract_ids_from_entity_value(v, ids);
}
}
EntityValue::EntityVecObject(vec_map) => {
for map in vec_map {
for v in map.values() {
Self::extract_ids_from_entity_value(v, ids);
}
}
}
EntityValue::NestedDynamicEntity(nested_value) => {
Self::extract_ids_from_entity_value(nested_value, ids);
}
_ => {}
}
}
}
impl CustomSerialize for MetadataDescriptor {
fn dynamic_entity(&self) -> Option<&HashMap<String, EntityValue>> {
self.dynamic_entity.as_ref()
}
fn id(&self) -> &String {
&self.id
}
fn type_(&self) -> &DataType {
&self.type_
}
fn conforms_to(&self) -> &Id {
&self.conforms_to
}
fn about(&self) -> &Id {
&self.about
}
}
impl Serialize for MetadataDescriptor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.custom_serialize(serializer)
}
}
pub trait CustomSerialize: Serialize {
fn dynamic_entity(&self) -> Option<&HashMap<String, EntityValue>>;
fn id(&self) -> &String;
fn type_(&self) -> &DataType;
fn conforms_to(&self) -> &Id;
fn about(&self) -> &Id;
fn custom_serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("@id", self.id())?;
map.serialize_entry("@type", self.type_())?;
map.serialize_entry("conformsTo", self.conforms_to())?;
map.serialize_entry("about", self.about())?;
if let Some(dynamic_entity) = self.dynamic_entity() {
for (k, v) in dynamic_entity {
map.serialize_entry(k, v)?;
}
}
map.end()
}
}
impl<'de> Deserialize<'de> for MetadataDescriptor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MetadataDescriptorVisitor;
impl<'de> Visitor<'de> for MetadataDescriptorVisitor {
type Value = MetadataDescriptor;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map representing a MetadataDescriptorEntity")
}
fn visit_map<A>(self, mut map: A) -> Result<MetadataDescriptor, A::Error>
where
A: MapAccess<'de>,
{
let mut id = None;
let mut type_ = None;
let mut conforms_to = None;
let mut about = None;
let mut dynamic_entity: HashMap<String, EntityValue> = HashMap::new();
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"@id" => id = Some(map.next_value()?),
"@type" => type_ = Some(map.next_value()?),
"conformsTo" => conforms_to = Some(map.next_value()?),
"about" => about = Some(map.next_value()?),
_ => {
let value: EntityValue = map.next_value()?;
dynamic_entity.insert(key, value);
}
}
}
let id = id.ok_or_else(|| de::Error::missing_field("@id"))?;
let type_ = type_.ok_or_else(|| de::Error::missing_field("@type"))?;
let conforms_to =
conforms_to.ok_or_else(|| de::Error::missing_field("conformsTo"))?;
let about = about.ok_or_else(|| de::Error::missing_field("about"))?;
Ok(MetadataDescriptor {
id,
type_,
conforms_to,
about,
dynamic_entity: Some(dynamic_entity),
})
}
}
deserializer.deserialize_map(MetadataDescriptorVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metadata_descriptor_creation() {
let metadata = MetadataDescriptor {
id: "metadata_id".to_string(),
type_: DataType::Term("MetadataType".to_string()),
conforms_to: Id::Id("conformsTo_id".to_string()),
about: Id::Id("about_id".to_string()),
dynamic_entity: Some(HashMap::new()),
};
assert_eq!(metadata.id, "metadata_id");
assert!(matches!(metadata.type_, DataType::Term(ref t) if t == "MetadataType"));
assert_eq!(metadata.conforms_to, Id::Id("conformsTo_id".to_string()));
assert_eq!(metadata.about, Id::Id("about_id".to_string()));
assert!(metadata.dynamic_entity.unwrap().is_empty());
}
#[test]
fn test_dynamic_entity_manipulation() {
let mut metadata = MetadataDescriptor {
id: "metadata_id".to_string(),
type_: DataType::Term("MetadataType".to_string()),
conforms_to: Id::Id("conformsTo_id".to_string()),
about: Id::Id("about_id".to_string()),
dynamic_entity: Some(HashMap::new()),
};
metadata.add_string_value("key1".to_string(), "value1".to_string());
assert_eq!(
metadata.dynamic_entity.as_ref().unwrap().get("key1"),
Some(&EntityValue::EntityString("value1".to_string()))
);
metadata.remove_field("key1");
assert!(metadata
.dynamic_entity
.as_ref()
.unwrap()
.get("key1")
.is_none());
}
#[test]
fn test_serialization() {
let metadata = MetadataDescriptor {
id: "metadata_id".to_string(),
type_: DataType::Term("MetadataType".to_string()),
conforms_to: Id::Id("conformsTo_id".to_string()),
about: Id::Id("about_id".to_string()),
dynamic_entity: Some(HashMap::new()),
};
let serialized = serde_json::to_string(&metadata).unwrap();
assert_eq!(
serialized,
r#"{"@id":"metadata_id","@type":"MetadataType","conformsTo":{"@id":"conformsTo_id"},"about":{"@id":"about_id"}}"#
);
}
#[test]
fn test_deserialization() {
let json_data = r#"
{
"@id": "metadata_id",
"@type": "MetadataType",
"conformsTo": {"@id": "conformsTo_id"},
"about": {"@id": "about_id"}
}
"#;
let deserialized: MetadataDescriptor = serde_json::from_str(json_data).unwrap();
assert_eq!(deserialized.id, "metadata_id");
assert!(matches!(deserialized.type_, DataType::Term(ref t) if t == "MetadataType"));
assert_eq!(
deserialized.conforms_to,
Id::Id("conformsTo_id".to_string())
);
assert_eq!(deserialized.about, Id::Id("about_id".to_string()));
}
#[test]
fn test_deserialization_with_missing_fields() {
let json_data = r#"
{
"@id": "metadata_id",
"@type": "MetadataType"
}
"#;
let result: Result<MetadataDescriptor, _> = serde_json::from_str(json_data);
assert!(result.is_err());
}
}