use crate::{
object,
syntax::{Keyword, Term},
util, Id, Indexed, Object, Objects, Reference, ToReference,
};
use cc_traits::MapInsert;
use generic_json::{JsonClone, JsonHash};
use iref::{Iri, IriBuf};
use std::collections::HashSet;
use std::convert::TryFrom;
use std::hash::{Hash, Hasher};
pub mod properties;
pub mod reverse_properties;
pub use properties::Properties;
pub use reverse_properties::ReverseProperties;
#[derive(PartialEq, Eq)]
pub struct Node<J: JsonHash, T: Id = IriBuf> {
pub(crate) id: Option<Reference<T>>,
pub(crate) types: Vec<Reference<T>>,
pub(crate) graph: Option<HashSet<Indexed<Object<J, T>>>>,
pub(crate) included: Option<HashSet<Indexed<Self>>>,
pub(crate) properties: Properties<J, T>,
pub(crate) reverse_properties: ReverseProperties<J, T>,
}
impl<J: JsonHash, T: Id> Default for Node<J, T> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl<J: JsonHash, T: Id> Node<J, T> {
#[inline(always)]
pub fn new() -> Self {
Self {
id: None,
types: Vec::new(),
graph: None,
included: None,
properties: Properties::new(),
reverse_properties: ReverseProperties::new(),
}
}
#[inline(always)]
pub fn with_id(id: Reference<T>) -> Self {
Self {
id: Some(id),
types: Vec::new(),
graph: None,
included: None,
properties: Properties::new(),
reverse_properties: ReverseProperties::new(),
}
}
#[inline(always)]
pub fn has_key(&self, key: &Term<T>) -> bool {
match key {
Term::Keyword(Keyword::Id) => self.id.is_some(),
Term::Keyword(Keyword::Type) => !self.types.is_empty(),
Term::Keyword(Keyword::Graph) => self.graph.is_some(),
Term::Keyword(Keyword::Included) => self.included.is_some(),
Term::Keyword(Keyword::Reverse) => !self.reverse_properties.is_empty(),
Term::Ref(prop) => self.properties.contains(prop),
_ => false,
}
}
#[inline(always)]
pub fn id(&self) -> Option<&Reference<T>> {
self.id.as_ref()
}
#[inline(always)]
pub fn as_iri(&self) -> Option<Iri> {
if let Some(id) = &self.id {
id.as_iri()
} else {
None
}
}
#[inline(always)]
pub fn as_str(&self) -> Option<&str> {
match self.as_iri() {
Some(iri) => Some(iri.into_str()),
None => None,
}
}
#[inline(always)]
pub fn types(&self) -> &[Reference<T>] {
self.types.as_ref()
}
#[inline]
pub fn has_type<U>(&self, ty: &U) -> bool
where
Reference<T>: PartialEq<U>,
{
for self_ty in &self.types {
if self_ty == ty {
return true;
}
}
false
}
#[inline]
pub fn is_empty(&self) -> bool {
self.types.is_empty()
&& self.graph.is_none()
&& self.included.is_none()
&& self.properties.is_empty()
&& self.reverse_properties.is_empty()
}
#[inline]
pub fn is_graph(&self) -> bool {
self.graph.is_some()
&& self.types.is_empty()
&& self.included.is_none()
&& self.properties.is_empty()
&& self.reverse_properties.is_empty()
}
#[inline(always)]
pub fn is_simple_graph(&self) -> bool {
self.id.is_none() && self.is_graph()
}
#[inline(always)]
pub fn graph(&self) -> Option<&HashSet<Indexed<Object<J, T>>>> {
self.graph.as_ref()
}
#[inline(always)]
pub fn graph_mut(&mut self) -> Option<&mut HashSet<Indexed<Object<J, T>>>> {
self.graph.as_mut()
}
#[inline(always)]
pub fn set_graph(&mut self, graph: Option<HashSet<Indexed<Object<J, T>>>>) {
self.graph = graph
}
#[inline(always)]
pub fn included(&self) -> Option<&HashSet<Indexed<Self>>> {
self.included.as_ref()
}
#[inline(always)]
pub fn included_mut(&mut self) -> Option<&mut HashSet<Indexed<Self>>> {
self.included.as_mut()
}
#[inline(always)]
pub fn set_included(&mut self, included: Option<HashSet<Indexed<Self>>>) {
self.included = included
}
#[inline(always)]
pub fn properties(&self) -> &Properties<J, T> {
&self.properties
}
#[inline(always)]
pub fn reverse_properties(&self) -> &ReverseProperties<J, T> {
&self.reverse_properties
}
#[inline(always)]
pub fn get<'a, Q: ToReference<T>>(&self, prop: Q) -> Objects<J, T>
where
T: 'a,
{
self.properties.get(prop)
}
#[inline(always)]
pub fn get_any<'a, Q: ToReference<T>>(&self, prop: Q) -> Option<&Indexed<Object<J, T>>>
where
T: 'a,
{
self.properties.get_any(prop)
}
#[inline(always)]
pub fn insert(&mut self, prop: Reference<T>, value: Indexed<Object<J, T>>) {
self.properties.insert(prop, value)
}
#[inline(always)]
pub fn insert_all<Objects: Iterator<Item = Indexed<Object<J, T>>>>(
&mut self,
prop: Reference<T>,
values: Objects,
) {
self.properties.insert_all(prop, values)
}
#[inline(always)]
pub fn insert_reverse(&mut self, reverse_prop: Reference<T>, reverse_value: Indexed<Self>) {
self.reverse_properties.insert(reverse_prop, reverse_value)
}
#[inline(always)]
pub fn insert_all_reverse<Nodes: Iterator<Item = Indexed<Self>>>(
&mut self,
reverse_prop: Reference<T>,
reverse_values: Nodes,
) {
self.reverse_properties
.insert_all(reverse_prop, reverse_values)
}
#[inline]
pub fn is_unnamed_graph(&self) -> bool {
self.graph.is_some()
&& self.id.is_none()
&& self.types.is_empty()
&& self.included.is_none()
&& self.properties.is_empty()
&& self.reverse_properties.is_empty()
}
#[inline(always)]
pub fn into_unnamed_graph(self) -> Result<HashSet<Indexed<Object<J, T>>>, Self> {
if self.is_unnamed_graph() {
Ok(self.graph.unwrap())
} else {
Err(self)
}
}
}
impl<J: JsonHash, T: Id> object::Any<J, T> for Node<J, T> {
#[inline(always)]
fn as_ref(&self) -> object::Ref<J, T> {
object::Ref::Node(self)
}
}
impl<J: JsonHash, T: Id> TryFrom<Object<J, T>> for Node<J, T> {
type Error = Object<J, T>;
#[inline(always)]
fn try_from(obj: Object<J, T>) -> Result<Node<J, T>, Object<J, T>> {
match obj {
Object::Node(node) => Ok(node),
obj => Err(obj),
}
}
}
impl<J: JsonHash, T: Id> Hash for Node<J, T> {
#[inline]
fn hash<H: Hasher>(&self, h: &mut H) {
self.id.hash(h);
self.types.hash(h);
util::hash_set_opt(&self.graph, h);
util::hash_set_opt(&self.included, h);
self.properties.hash(h);
self.reverse_properties.hash(h)
}
}
impl<J: JsonHash + JsonClone, K: util::JsonFrom<J>, T: Id> util::AsJson<J, K> for Node<J, T> {
fn as_json_with(&self, meta: impl Clone + Fn(Option<&J::MetaData>) -> K::MetaData) -> K {
let mut obj = K::Object::default();
if let Some(id) = &self.id {
obj.insert(
K::new_key(Keyword::Id.into_str(), meta(None)),
id.as_json_with(meta.clone()),
);
}
if !self.types.is_empty() {
obj.insert(
K::new_key(Keyword::Type.into_str(), meta(None)),
self.types.as_json_with(meta.clone()),
);
}
if let Some(graph) = &self.graph {
obj.insert(
K::new_key(Keyword::Graph.into_str(), meta(None)),
graph.as_json_with(meta.clone()),
);
}
if let Some(included) = &self.included {
obj.insert(
K::new_key(Keyword::Included.into_str(), meta(None)),
included.as_json_with(meta.clone()),
);
}
if !self.reverse_properties.is_empty() {
let mut reverse = K::Object::default();
for (key, value) in &self.reverse_properties {
reverse.insert(
K::new_key(key.as_str(), meta(None)),
value.as_json_with(meta.clone()),
);
}
obj.insert(
K::new_key(Keyword::Reverse.into_str(), meta(None)),
K::object(reverse, meta(None)),
);
}
for (key, value) in &self.properties {
obj.insert(
K::new_key(key.as_str(), meta(None)),
value.as_json_with(meta.clone()),
);
}
K::object(obj, meta(None))
}
}
impl<J: JsonHash + JsonClone, K: util::JsonFrom<J>, T: Id> util::AsJson<J, K>
for HashSet<Indexed<Node<J, T>>>
{
#[inline(always)]
fn as_json_with(&self, meta: impl Clone + Fn(Option<&J::MetaData>) -> K::MetaData) -> K {
let array = self
.iter()
.map(|value| value.as_json_with(meta.clone()))
.collect();
K::array(array, meta(None))
}
}
pub struct Nodes<'a, J: JsonHash, T: Id>(Option<std::slice::Iter<'a, Indexed<Node<J, T>>>>);
impl<'a, J: JsonHash, T: Id> Nodes<'a, J, T> {
#[inline(always)]
pub(crate) fn new(inner: Option<std::slice::Iter<'a, Indexed<Node<J, T>>>>) -> Self {
Self(inner)
}
}
impl<'a, J: JsonHash, T: Id> Iterator for Nodes<'a, J, T> {
type Item = &'a Indexed<Node<J, T>>;
#[inline(always)]
fn next(&mut self) -> Option<&'a Indexed<Node<J, T>>> {
match &mut self.0 {
None => None,
Some(it) => it.next(),
}
}
}