use crate::ro_crate::constraints::{DynamicEntity, Id, IdValue};
use crate::ro_crate::contextual_entity::ContextualEntity;
use crate::ro_crate::data_entity::DataEntity;
use crate::ro_crate::metadata_descriptor::MetadataDescriptor;
use crate::ro_crate::modify::DynamicEntityManipulation;
use crate::ro_crate::root::RootDataEntity;
use serde::de::Error as SerdeError;
use serde::ser::Serializer;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::{Error as SerdeJsonError, Value};
use std::collections::HashMap;
use std::fmt;
use std::path::Path;
use url::Url;
#[derive(Serialize, Deserialize, Debug)]
pub struct RoCrate {
#[serde(rename = "@context")]
pub context: RoCrateContext,
#[serde(rename = "@graph")]
pub graph: Vec<GraphVector>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum RoCrateContext {
ReferenceContext(String),
ExtendedContext(Vec<ContextItem>),
EmbeddedContext(Vec<HashMap<String, String>>),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum ContextItem {
ReferenceItem(String),
EmbeddedContext(HashMap<String, String>),
}
impl RoCrate {
pub fn new(context: RoCrateContext, _graph: Vec<GraphVector>) -> RoCrate {
RoCrate {
context,
graph: Vec::new(),
}
}
pub fn remove_by_id(&mut self, id_to_remove: &str, rec: bool) {
self.graph
.retain(|graph_vector: &GraphVector| match graph_vector {
GraphVector::MetadataDescriptor(descriptor) => &descriptor.id != id_to_remove,
GraphVector::RootDataEntity(entity) => &entity.id != id_to_remove,
GraphVector::DataEntity(entity) => &entity.id != id_to_remove,
GraphVector::ContextualEntity(entity) => &entity.id != id_to_remove,
GraphVector::FallbackValue(_) => true,
});
if rec == true {
self.remove_id_recursive(id_to_remove)
}
}
fn remove_id_recursive(&mut self, id: &str) {
for graph_vector in &mut self.graph {
if let GraphVector::RootDataEntity(data_entity) = graph_vector {
data_entity.remove_matching_value(id);
}
if let GraphVector::MetadataDescriptor(data_entity) = graph_vector {
data_entity.remove_matching_value(id);
}
if let GraphVector::DataEntity(data_entity) = graph_vector {
data_entity.remove_matching_value(id);
}
if let GraphVector::ContextualEntity(data_entity) = graph_vector {
data_entity.remove_matching_value(id);
}
}
}
pub fn update_id_recursive(&mut self, id_old: &str, id_new: &str) -> Option<()> {
let mut any_updates = false;
for graph_vector in &mut self.graph {
let updated = match graph_vector {
GraphVector::ContextualEntity(entity) => {
if entity.id == id_old {
entity.id = id_new.to_string();
}
entity.update_matching_id(id_old, id_new)
}
GraphVector::DataEntity(entity) => {
if entity.id == id_old {
entity.id = id_new.to_string();
}
entity.update_matching_id(id_old, id_new)
}
GraphVector::RootDataEntity(entity) => {
if entity.id == id_old {
entity.id = id_new.to_string();
}
entity.update_matching_id(id_old, id_new)
}
GraphVector::MetadataDescriptor(descriptor) => {
if descriptor.id == id_old {
descriptor.id = id_new.to_string();
}
descriptor.update_matching_id(id_old, id_new)
}
_ => None,
};
if updated.is_some() {
any_updates = true;
}
}
if any_updates {
Some(())
} else {
None
}
}
pub fn find_id_index(&mut self, id: &str) -> Option<usize> {
self.graph
.iter()
.enumerate()
.find_map(|(index, graph_vector)| match graph_vector {
GraphVector::MetadataDescriptor(descriptor) if descriptor.id == id => Some(index),
GraphVector::RootDataEntity(entity) if entity.id == id => Some(index),
GraphVector::DataEntity(entity) if entity.id == id => Some(index),
GraphVector::ContextualEntity(entity) if entity.id == id => Some(index),
_ => None,
})
}
pub fn find_id(&mut self, id: &str) -> Option<&GraphVector> {
self.find_id_index(id)
.and_then(|index| self.graph.get(index))
}
pub fn remove_dynamic_entity_field(&mut self, id: &str, key: &str) {
let index = self.find_id_index(id);
if let Some(index) = index {
if let Some(graph_vector) = self.graph.get_mut(index) {
match graph_vector {
GraphVector::MetadataDescriptor(descriptor) => {
if let Some(dynamic_entity) = &mut descriptor.dynamic_entity {
dynamic_entity.remove(key);
}
}
GraphVector::RootDataEntity(entity) => {
if let Some(dynamic_entity) = &mut entity.dynamic_entity {
dynamic_entity.remove(key);
}
}
GraphVector::DataEntity(entity) => {
if let Some(dynamic_entity) = &mut entity.dynamic_entity {
dynamic_entity.remove(key);
}
}
GraphVector::ContextualEntity(entity) => {
if let Some(dynamic_entity) = &mut entity.dynamic_entity {
dynamic_entity.remove(key);
}
}
GraphVector::FallbackValue(_) => {} };
}
}
}
pub fn add_dynamic_entity_field(&mut self, id: &str, values: HashMap<String, DynamicEntity>) {
if let Some(index) = self.find_id_index(id) {
if let Some(graph_vector) = self.graph.get_mut(index) {
match graph_vector {
GraphVector::MetadataDescriptor(descriptor) => {
descriptor.add_dynamic_fields(values)
}
GraphVector::RootDataEntity(entity) => entity.add_dynamic_fields(values),
GraphVector::DataEntity(entity) => entity.add_dynamic_fields(values),
GraphVector::ContextualEntity(entity) => entity.add_dynamic_fields(values),
GraphVector::FallbackValue(_) => {} };
}
}
}
pub fn get_property_value(&self, key: String) -> Vec<String> {
let mut property_values: Vec<String> = Vec::new();
for graph_vector in &self.graph {
match graph_vector {
GraphVector::ContextualEntity(ref _entity) => {
let keys = _entity.search_keys(&key.to_string(), false);
if keys.len() != 0 {
property_values.extend(keys);
}
}
GraphVector::DataEntity(ref _entity) => {
let keys = _entity.search_keys(&key.to_string(), false);
if keys.len() != 0 {
property_values.extend(keys);
}
}
GraphVector::RootDataEntity(_entity) => {
let keys = _entity.search_keys(&key.to_string(), false);
if keys.len() != 0 {
property_values.extend(keys);
}
}
GraphVector::MetadataDescriptor(_entity) => {
let keys = _entity.search_keys(&key.to_string(), false);
if keys.len() != 0 {
property_values.extend(keys);
}
}
GraphVector::FallbackValue(_entity) => {
todo!();
}
}
}
dedup_vec(&mut property_values);
property_values
}
pub fn get_all_property_values(&self) -> Vec<String> {
let mut property_values: Vec<String> = Vec::new();
let key: String = String::new();
for graph_vector in &self.graph {
match graph_vector {
GraphVector::ContextualEntity(ref _entity) => {
let keys = _entity.search_keys(&key, true);
if keys.len() != 0 {
property_values.extend(keys);
}
}
GraphVector::DataEntity(ref _entity) => {
let keys = _entity.search_keys(&key, true);
if keys.len() != 0 {
property_values.extend(keys);
}
}
GraphVector::RootDataEntity(_entity) => {
let keys = _entity.search_keys(&key, true);
if keys.len() != 0 {
property_values.extend(keys);
}
}
GraphVector::MetadataDescriptor(_entity) => {
let keys = _entity.search_keys(&key, true);
if keys.len() != 0 {
property_values.extend(keys);
}
}
GraphVector::FallbackValue(_entity) => {
todo!();
}
}
}
dedup_vec(&mut property_values);
property_values
}
pub fn get_context_items(&self) -> Vec<String> {
let mut valid_context: Vec<String> = Vec::new();
match &self.context {
RoCrateContext::EmbeddedContext(context) => {
for map in context {
for (key, _value) in map {
valid_context.push(key.to_string());
}
}
}
RoCrateContext::ExtendedContext(context) => {
for map in context {
match map {
ContextItem::EmbeddedContext(context) => {
for (key, _value) in context {
valid_context.push(key.to_string());
}
}
_ => (),
}
}
}
RoCrateContext::ReferenceContext(context) => {
valid_context.push(context.to_string());
}
}
valid_context
}
pub fn add_context(&self) {}
pub fn overwite_by_id(&mut self, id: &str, new_data: GraphVector) {
if let Some(index) = self.find_id_index(id) {
self.graph[index] = new_data;
if let GraphVector::DataEntity(_entity) = &self.graph[index] {
self.add_data_to_partof_root(id)
}
} else {
self.graph.push(new_data);
if let Some(index) = self.find_id_index(id) {
if let GraphVector::DataEntity(_entity) = &self.graph[index] {
self.add_data_to_partof_root(id)
}
}
}
}
pub fn add_data_to_partof_root(&mut self, target_id: &str) {
if let Some(index) = self.find_id_index("./") {
if let Some(GraphVector::RootDataEntity(root)) = self.graph.get_mut(index) {
let dynamic_entity = root.dynamic_entity.get_or_insert_with(HashMap::new);
match dynamic_entity.get_mut("hasPart") {
Some(DynamicEntity::EntityId(Id::IdArray(ref mut id_array))) => {
if !id_array.iter().any(|id_value| id_value.id == target_id) {
id_array.push(IdValue {
id: target_id.to_owned(),
});
}
}
_ => {
dynamic_entity.insert(
"hasPart".to_string(),
DynamicEntity::EntityId(Id::IdArray(vec![IdValue {
id: target_id.to_owned(),
}])),
);
}
};
};
};
}
pub fn get_all_ids(&self) -> Vec<&String> {
let mut id_vec: Vec<&String> = Vec::new();
for graph_vector in self.graph.iter() {
match graph_vector {
GraphVector::MetadataDescriptor(entity) => id_vec.push(&entity.id),
GraphVector::RootDataEntity(entity) => id_vec.push(&entity.id),
GraphVector::DataEntity(entity) => id_vec.push(&entity.id),
GraphVector::ContextualEntity(entity) => id_vec.push(&entity.id),
GraphVector::FallbackValue(_) => {} };
}
id_vec
}
}
fn dedup_vec<T: Ord>(vec: &mut Vec<T>) {
vec.sort();
vec.dedup();
}
impl Default for RoCrate {
fn default() -> Self {
RoCrate {
context: RoCrateContext::ReferenceContext(String::from(
"https://w3id.org/ro/crate/1.1/context",
)),
graph: Vec::new(),
}
}
}
impl fmt::Display for RoCrate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"RO-Crate: context={:?}, graph={:?}",
self.context, self.graph
)
}
}
#[derive(Debug)]
pub enum GraphVector {
DataEntity(DataEntity),
ContextualEntity(ContextualEntity),
FallbackValue(Value),
MetadataDescriptor(MetadataDescriptor),
RootDataEntity(RootDataEntity),
}
impl Serialize for GraphVector {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
GraphVector::MetadataDescriptor(md) => md.serialize(serializer),
GraphVector::RootDataEntity(rde) => rde.serialize(serializer),
GraphVector::DataEntity(de) => de.serialize(serializer),
GraphVector::ContextualEntity(ce) => ce.serialize(serializer),
GraphVector::FallbackValue(fv) => fv.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for GraphVector {
fn deserialize<D>(deserializer: D) -> Result<GraphVector, D::Error>
where
D: Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
try_deserialize_into_graph_vector(&value)
.or_else(|e| {
return Err(e);
})
.map_err(|e: SerdeJsonError| {
D::Error::custom(format!("Failed to deserialize: {}", e))
})
}
}
fn try_deserialize_into_graph_vector(value: &Value) -> Result<GraphVector, SerdeJsonError> {
if let Some(id) = value.get("@id").and_then(Value::as_str) {
match id {
"ro-crate-metadata.json" => {
MetadataDescriptor::deserialize(value).map(GraphVector::MetadataDescriptor)
}
"./" => RootDataEntity::deserialize(value).map(GraphVector::RootDataEntity),
_ => {
if is_valid_url_or_path(id) {
DataEntity::deserialize(value).map(GraphVector::DataEntity)
} else {
ContextualEntity::deserialize(value).map(GraphVector::ContextualEntity)
}
}
}
} else {
Err(serde::de::Error::custom("Missing or invalid '@id' field"))
}
}
fn is_valid_url_or_path(s: &str) -> bool {
Url::parse(s).is_ok() || Path::new(s).exists()
}