use serde::de::{self, MapAccess, SeqAccess, Visitor};
use serde::ser::{SerializeMap, SerializeSeq};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum Id {
Id(String),
IdArray(Vec<String>),
}
impl Id {
pub fn contains_id(&self, target_id: &str) -> bool {
match self {
Id::Id(id_value) => id_value == target_id,
Id::IdArray(id_values) => id_values.iter().any(|id_val| id_val == target_id),
}
}
}
impl Serialize for Id {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Id::Id(id_value) => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("@id", id_value)?;
map.end()
}
Id::IdArray(id_values) => {
let mut seq = serializer.serialize_seq(Some(id_values.len()))?;
for id in id_values {
let mut temp_map = HashMap::new();
temp_map.insert("@id", id);
seq.serialize_element(&temp_map)?;
}
seq.end()
}
}
}
}
impl<'de> Deserialize<'de> for Id {
fn deserialize<D>(deserializer: D) -> Result<Id, D::Error>
where
D: Deserializer<'de>,
{
struct IdVisitor;
impl<'de> Visitor<'de> for IdVisitor {
type Value = Id;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an @id entry or an array of @id entries")
}
fn visit_map<A>(self, mut map: A) -> Result<Id, A::Error>
where
A: MapAccess<'de>,
{
let key: Option<String> = map.next_key()?;
let id_value: String = map.next_value()?;
if let Some(key) = key {
if key == "@id" {
return Ok(Id::Id(id_value));
}
}
Err(de::Error::missing_field("@id"))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Id, A::Error>
where
A: SeqAccess<'de>,
{
let mut ids = Vec::new();
while let Some(map) = seq.next_element::<HashMap<String, String>>()? {
if let Some(id) = map.get("@id") {
ids.push(id.clone());
} else {
return Err(de::Error::missing_field("@id"));
}
}
Ok(Id::IdArray(ids))
}
}
deserializer.deserialize_any(IdVisitor)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum License {
Id(Id),
Description(String),
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(untagged)]
pub enum DataType {
Term(String),
TermArray(Vec<String>),
}
impl DataType {
pub fn add_type(&self, new_type: String) {
match self {
DataType::Term(existing_type) => {
DataType::TermArray(vec![existing_type.clone(), new_type]);
}
DataType::TermArray(existing_types) => {
let mut updated_types = existing_types.clone();
updated_types.push(new_type);
DataType::TermArray(updated_types);
}
}
}
pub fn remove_type(&self, remove: String) -> Result<DataType, String> {
match self {
DataType::Term(_) => Err(
"Cannot remove the only data type; at least one data type is required.".to_string(),
),
DataType::TermArray(existing_types) => {
if existing_types.len() == 1 && existing_types[0] == remove {
return Err(
"Cannot remove the only data type; at least one data type is required."
.to_string(),
);
}
if let Some(index) = existing_types.iter().position(|n| n == &remove) {
let mut updated_types = existing_types.clone();
updated_types.remove(index);
if updated_types.len() == 1 {
Ok(DataType::Term(updated_types[0].clone()))
} else {
Ok(DataType::TermArray(updated_types))
}
} else {
Err(format!("Data type '{}' not found in the list.", remove))
}
}
}
}
pub fn modify_type(&self) {}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[serde(untagged)]
pub enum EntityValue {
EntityString(String),
EntityVecString(Vec<String>),
EntityId(Id),
Entityi64(i64),
Entityf64(f64),
EntityVeci64(Vec<i64>),
EntityVecf64(Vec<f64>),
EntityLicense(License),
EntityDataType(DataType),
EntityNull(Option<bool>),
EntityBool(bool),
EntityNone(Option<String>),
EntityVec(Vec<EntityValue>),
EntityObject(HashMap<String, EntityValue>),
EntityVecObject(Vec<HashMap<String, EntityValue>>),
NestedDynamicEntity(Box<EntityValue>),
Fallback(Option<serde_json::Value>),
}
impl fmt::Display for EntityValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EntityValue::EntityString(value) => write!(f, "EntityString({})", value),
EntityValue::EntityVecString(values) => write!(f, "EntityVecString({:?})", values),
EntityValue::EntityId(value) => write!(f, "EntityId({:?})", value),
EntityValue::EntityLicense(value) => write!(f, "EntityLicense({:?})", value),
EntityValue::Entityi64(value) => write!(f, "Entityi64({})", value),
EntityValue::Entityf64(value) => write!(f, "Entityf64({})", value),
EntityValue::EntityVeci64(values) => write!(f, "EntityVeci64({:?})", values),
EntityValue::EntityVecf64(values) => write!(f, "EntityVecf64({:?})", values),
EntityValue::EntityDataType(value) => write!(f, "EntityDataType({:?})", value),
EntityValue::EntityBool(value) => write!(f, "EntityBool({:?})", value),
EntityValue::EntityVec(values) => write!(f, "EntityVec({:?})", values),
EntityValue::EntityObject(object) => write!(f, "EntityObject({:?})", object),
EntityValue::EntityVecObject(objects) => write!(f, "EntityVecObject({:?})", objects),
EntityValue::NestedDynamicEntity(value) => write!(f, "NestedDynamicEntity({})", value),
EntityValue::EntityNull(value) => write!(f, "EntityNull: {:?}", value),
EntityValue::EntityNone(value) => write!(f, "EntityNone: {:?}", value),
EntityValue::Fallback(value) => write!(f, "Fallback({:?})", value),
}
}
}
impl EntityValue {
pub fn parse(input: &str) -> Option<EntityValue> {
if let Ok(parsed) = serde_json::from_str::<String>(input) {
return Some(EntityValue::EntityString(parsed));
}
if let Ok(parsed) = serde_json::from_str::<String>(&format!(r#""{}""#, input)) {
return Some(EntityValue::EntityString(parsed));
}
if let Ok(parsed) = serde_json::from_str::<i64>(input) {
return Some(EntityValue::Entityi64(parsed));
}
if let Ok(parsed) = serde_json::from_str::<f64>(input) {
return Some(EntityValue::Entityf64(parsed));
}
if let Ok(parsed) = serde_json::from_str::<bool>(input) {
return Some(EntityValue::EntityBool(parsed));
}
if let Ok(parsed) = serde_json::from_str::<Vec<String>>(input) {
return Some(EntityValue::EntityVecString(parsed));
}
if let Ok(parsed) = serde_json::from_str::<Vec<i64>>(input) {
return Some(EntityValue::EntityVeci64(parsed));
}
if let Ok(parsed) = serde_json::from_str::<Vec<f64>>(input) {
return Some(EntityValue::EntityVecf64(parsed));
}
if let Ok(parsed) = serde_json::from_str::<HashMap<String, EntityValue>>(input) {
return Some(EntityValue::EntityObject(parsed));
}
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(input) {
return Some(EntityValue::Fallback(Some(parsed)));
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_contains_id_single() {
let id = Id::Id("test_id".to_string());
assert!(id.contains_id("test_id"));
assert!(!id.contains_id("other_id"));
}
#[test]
fn test_contains_id_in_array() {
let ids = Id::IdArray(vec!["test_id1".to_string(), "test_id2".to_string()]);
assert!(ids.contains_id("test_id1"));
assert!(ids.contains_id("test_id2"));
assert!(!ids.contains_id("other_id"));
}
#[test]
fn test_contains_id_empty_string() {
let id = Id::Id("".to_string());
assert!(!id.contains_id("test_id"));
}
#[test]
fn test_serialization_deserialization_id() {
let id = Id::Id("test_id".to_string());
let serialized = serde_json::to_string(&id).unwrap();
let deserialized: Id = serde_json::from_str(&serialized).unwrap();
assert_eq!(serialized, r#"{"@id":"test_id"}"#);
assert!(deserialized.contains_id("test_id"));
}
#[test]
fn test_dynamic_entity_fallback() {
let json_value = json!({"unexpected": "data"});
let entity = EntityValue::Fallback(Some(json_value.clone()));
match entity {
EntityValue::Fallback(Some(value)) => assert_eq!(value, json_value),
_ => panic!("Fallback variant with expected value was not found"),
}
}
#[test]
fn test_dynamic_entity_string() {
let entity = EntityValue::EntityString("test".to_string());
match entity {
EntityValue::EntityString(value) => assert_eq!(value, "test"),
_ => panic!("EntityString variant expected"),
}
}
#[test]
fn test_dynamic_entity_bool() {
let entity = EntityValue::EntityBool(true);
match entity {
EntityValue::EntityBool(value) => assert!(value),
_ => panic!("EntityBool variant expected"),
}
}
#[test]
fn test_dynamic_entity_id() {
let id = Id::Id("entity_id".to_string());
let entity = EntityValue::EntityId(id.clone());
match entity {
EntityValue::EntityId(e_id) => assert_eq!(e_id, id),
_ => panic!("EntityId variant expected"),
}
}
#[test]
fn test_dynamic_entity_i64() {
let entity = EntityValue::Entityi64(42);
match entity {
EntityValue::Entityi64(value) => assert_eq!(value, 42),
_ => panic!("Entityi64 variant expected"),
}
}
#[test]
fn test_dynamic_entity_f64() {
let entity = EntityValue::Entityf64(3.14);
match entity {
EntityValue::Entityf64(value) => assert!((value - 3.14).abs() < f64::EPSILON),
_ => panic!("Entityf64 variant expected"),
}
}
#[test]
fn test_dynamic_entity_fallback_empty() {
let json_value = json!({});
let entity = EntityValue::Fallback(Some(json_value.clone()));
match entity {
EntityValue::Fallback(Some(value)) => assert_eq!(value, json_value),
_ => panic!("Fallback variant with expected value was not found"),
}
}
#[test]
fn test_license_id() {
let license = License::Id(Id::Id("license_id".to_string()));
match license {
License::Id(Id::Id(id_value)) => assert_eq!(id_value, "license_id"),
_ => panic!("License::Id variant expected"),
}
}
#[test]
fn test_license_description() {
let license = License::Description("Creative Commons".to_string());
match license {
License::Description(desc) => assert_eq!(desc, "Creative Commons"),
_ => panic!("License::Description variant expected"),
}
}
#[test]
fn test_data_type_term() {
let data_type = DataType::Term("Text".to_string());
match data_type {
DataType::Term(term) => assert_eq!(term, "Text"),
_ => panic!("DataType::Term variant expected"),
}
}
#[test]
fn test_data_type_term_array() {
let data_type = DataType::TermArray(vec!["Text".to_string(), "Image".to_string()]);
match data_type {
DataType::TermArray(terms) => assert_eq!(terms, vec!["Text", "Image"]),
_ => panic!("DataType::TermArray variant expected"),
}
}
}