use crate::ro_crate::constraints::*;
use serde::ser::SerializeMap;
use serde::{Serialize, Serializer};
use serde_json::Value;
use std::collections::HashMap;
pub trait DynamicEntityManipulation: Serialize {
fn dynamic_entity(&mut self) -> &mut Option<HashMap<String, DynamicEntity>>;
fn dynamic_entity_immut(&self) -> &Option<HashMap<String, DynamicEntity>>;
fn add_dynamic_fields(&mut self, values: HashMap<String, DynamicEntity>) {
for (key, value) in values {
match value {
DynamicEntity::EntityString(s) => self.add_string_value(key, s),
DynamicEntity::EntityId(id) => self.add_id_value(key, id),
_ => todo!(),
}
}
}
fn add_string_value(&mut self, key: String, value: String) {
self.dynamic_entity()
.get_or_insert_with(HashMap::new)
.insert(key, DynamicEntity::EntityString(value));
}
fn add_id_value(&mut self, key: String, value: Id) {
self.dynamic_entity()
.get_or_insert_with(HashMap::new)
.insert(key, DynamicEntity::EntityId(value));
}
fn remove_field(&mut self, key: &str) {
if let Some(dynamic_entity) = self.dynamic_entity() {
dynamic_entity.remove(key);
}
}
fn search_value(&self, search_value: &DynamicEntity) -> bool {
if let Some(dynamic_entity) = self.dynamic_entity_immut() {
for (_key, value) in dynamic_entity.iter() {
if value == search_value {
return true;
}
}
}
return false;
}
fn search_keys(&self, search_key: &String, get_all: bool) -> Vec<String> {
let mut key_vec: Vec<String> = Vec::new();
fn search_obj(
object: &HashMap<String, DynamicEntity>,
search_key: &String,
get_all: bool,
) -> Vec<String> {
let mut key_vec: Vec<String> = Vec::new();
for (_key, value) in object.iter() {
if get_all || _key == search_key {
key_vec.push(_key.clone()); }
if let DynamicEntity::EntityObject(inner_object) = value {
let inner_keys = search_obj(inner_object, search_key, get_all);
if !inner_keys.is_empty() {
key_vec.extend(inner_keys);
}
}
}
key_vec
}
if let Some(dynamic_entity) = self.dynamic_entity_immut() {
for (_key, value) in dynamic_entity.iter() {
if let DynamicEntity::EntityObject(object) = value {
let obvec = search_obj(object, search_key, get_all);
if !obvec.is_empty() {
key_vec.extend(obvec);
}
}
if get_all {
key_vec.push(_key.to_string());
}
}
}
key_vec
}
fn remove_matching_value(&mut self, target_id: &str) {
if let Some(dynamic_entity) = self.dynamic_entity() {
let mut keys_to_remove = Vec::new();
let mut fallback_keys_to_modify = Vec::new();
let mut updates = Vec::new();
for (key, value) in dynamic_entity.iter() {
match value {
DynamicEntity::EntityString(s) if s == target_id => {
keys_to_remove.push(key.clone());
}
DynamicEntity::EntityId(Id::IdArray(id_values)) => {
let filtered_values: Vec<IdValue> = id_values
.iter()
.filter(|id_val| id_val.id != target_id)
.cloned()
.collect();
if filtered_values.len() != id_values.len() {
updates.push((key.clone(), Id::IdArray(filtered_values)));
}
}
DynamicEntity::EntityId(Id::Id(id_value)) if id_value.id == target_id => {
keys_to_remove.push(key.clone());
}
DynamicEntity::Fallback(fallback_values) => {
println!("Exploring fallback {:?}", fallback_values);
fallback_keys_to_modify.push(key.clone());
}
_ => {
}
}
}
for key in fallback_keys_to_modify {
if let Some(DynamicEntity::Fallback(fallback_value)) = dynamic_entity.get_mut(&key)
{
remove_matching_value_from_json(fallback_value, target_id);
}
}
for (key, updated_id) in updates {
if let Some(DynamicEntity::EntityId(id)) = dynamic_entity.get_mut(&key) {
*id = updated_id;
}
}
for key in keys_to_remove {
dynamic_entity.remove(&key);
}
}
}
fn update_matching_id(&mut self, id_old: &str, id_new: &str) -> Option<()> {
let mut updated = false;
if let Some(dynamic_entity) = &mut self.dynamic_entity() {
for (_key, value) in dynamic_entity.iter_mut() {
match value {
DynamicEntity::EntityId(Id::IdArray(ids)) => {
for id in ids {
if id.id == id_old {
id.id = id_new.to_string();
updated = true;
}
}
}
DynamicEntity::EntityId(Id::Id(id)) => {
if id.id == id_old {
id.id = id_new.to_string();
updated = true;
}
}
DynamicEntity::EntityIdVec(ids) => {
for _id in ids {
}
}
_ => (),
}
}
}
if updated {
Some(())
} else {
None
}
}
}
fn remove_matching_value_from_json(value: &mut Value, target_value: &str) {
match value {
Value::Object(obj) => {
let keys_to_remove: Vec<String> = obj
.iter()
.filter_map(|(k, v)| {
if v == target_value {
Some(k.clone())
} else {
None
}
})
.collect();
for key in keys_to_remove {
obj.remove(&key);
}
for v in obj.values_mut() {
remove_matching_value_from_json(v, target_value);
}
}
Value::Array(arr) => {
let mut i = 0;
while i < arr.len() {
if &arr[i] == target_value {
arr.remove(i);
} else {
remove_matching_value_from_json(&mut arr[i], target_value);
i += 1;
}
}
}
_ => {}
}
}
pub trait CustomSerialize: Serialize {
fn dynamic_entity(&self) -> Option<&HashMap<String, DynamicEntity>>;
fn id(&self) -> &String;
fn type_(&self) -> &DataType;
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_())?;
if let Some(dynamic_entity) = self.dynamic_entity() {
for (k, v) in dynamic_entity {
map.serialize_entry(k, v)?;
}
}
map.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
struct TestEntity {
pub id: String,
pub type_: DataType,
pub dynamic_entity: Option<HashMap<String, DynamicEntity>>,
}
impl DynamicEntityManipulation for TestEntity {
fn dynamic_entity(&mut self) -> &mut Option<HashMap<String, DynamicEntity>> {
&mut self.dynamic_entity
}
fn dynamic_entity_immut(&self) -> &Option<HashMap<String, DynamicEntity>> {
&self.dynamic_entity
}
}
impl CustomSerialize for TestEntity {
fn dynamic_entity(&self) -> Option<&HashMap<String, DynamicEntity>> {
self.dynamic_entity.as_ref()
}
fn id(&self) -> &String {
&self.id
}
fn type_(&self) -> &DataType {
&self.type_
}
}
impl Serialize for TestEntity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.custom_serialize(serializer)
}
}
#[test]
fn test_add_string_value() {
let mut entity = TestEntity {
id: "test_id".to_string(),
type_: DataType::Term("test_type".to_string()),
dynamic_entity: None,
};
entity.add_string_value("key1".to_string(), "value1".to_string());
assert_eq!(
entity.dynamic_entity.unwrap().get("key1").unwrap(),
&DynamicEntity::EntityString("value1".to_string())
);
}
#[test]
fn test_remove_field() {
let mut entity = TestEntity {
id: "test_id".to_string(),
type_: DataType::Term("test_type".to_string()),
dynamic_entity: Some(HashMap::from([(
"key1".to_string(),
DynamicEntity::EntityString("value1".to_string()),
)])),
};
entity.remove_field("key1");
assert!(entity.dynamic_entity.unwrap().get("key1").is_none());
}
#[test]
fn test_remove_matching_value() {
let mut entity = TestEntity {
id: "test_id".to_string(),
type_: DataType::Term("test_type".to_string()),
dynamic_entity: Some(HashMap::from([
(
"key1".to_string(),
DynamicEntity::EntityString("value1".to_string()),
),
(
"key2".to_string(),
DynamicEntity::EntityString("value1".to_string()),
),
])),
};
entity.remove_matching_value("value1");
assert!(entity.dynamic_entity.unwrap().is_empty());
}
#[test]
fn test_custom_serialize() {
let entity = TestEntity {
id: "test_id".to_string(),
type_: DataType::Term("test_type".to_string()),
dynamic_entity: Some(HashMap::from([(
"key1".to_string(),
DynamicEntity::EntityString("value1".to_string()),
)])),
};
let serialized = serde_json::to_string(&entity).unwrap();
let expected_json = json!({
"@id": "test_id",
"@type": "test_type",
"key1": "value1",
})
.to_string();
assert_eq!(serialized, expected_json);
}
}