json-ld-core 0.12.1

A JSON-LD implementation
Documentation
//! Flattening algorithm and related types.
use crate::flattened::UnorderedFlattenedDocument;
use crate::{
	id, ExpandedDocument, FlattenedDocument, IndexedNode, IndexedObject, Object,
	StrippedIndexedNode,
};
use contextual::WithContext;
use json_ld_syntax::Entry;
use locspan::{Meta, Stripped};
use rdf_types::Vocabulary;
use std::collections::HashSet;
use std::hash::Hash;

mod environment;
mod node_map;

pub use environment::Environment;
pub use node_map::*;

pub type FlattenResult<I, B, M> =
	Result<Meta<FlattenedDocument<I, B, M>, M>, ConflictingIndexes<I, B, M>>;

pub type FlattenUnorderedResult<I, B, M> =
	Result<Meta<UnorderedFlattenedDocument<I, B, M>, M>, ConflictingIndexes<I, B, M>>;

pub trait FlattenMeta<I, B, M> {
	fn flatten_meta<V, G: id::Generator<V, M>>(
		self,
		meta: M,
		vocabulary: &mut V,
		generator: G,
		ordered: bool,
	) -> FlattenResult<I, B, M>
	where
		V: Vocabulary<Iri = I, BlankId = B>;

	fn flatten_unordered_meta<V, G: id::Generator<V, M>>(
		self,
		meta: M,
		vocabulary: &mut V,
		generator: G,
	) -> FlattenUnorderedResult<I, B, M>
	where
		V: Vocabulary<Iri = I, BlankId = B>;
}

pub trait Flatten<I, B, M> {
	fn flatten_with<V, G: id::Generator<V, M>>(
		self,
		vocabulary: &mut V,
		generator: G,
		ordered: bool,
	) -> FlattenResult<I, B, M>
	where
		V: Vocabulary<Iri = I, BlankId = B>;

	fn flatten_unordered_with<V, G: id::Generator<V, M>>(
		self,
		vocabulary: &mut V,
		generator: G,
	) -> FlattenUnorderedResult<I, B, M>
	where
		V: Vocabulary<Iri = I, BlankId = B>;

	fn flatten<G: id::Generator<(), M>>(self, generator: G, ordered: bool) -> FlattenResult<I, B, M>
	where
		(): Vocabulary<Iri = I, BlankId = B>,
		Self: Sized,
	{
		self.flatten_with(
			rdf_types::vocabulary::no_vocabulary_mut(),
			generator,
			ordered,
		)
	}

	fn flatten_unordered<G: id::Generator<(), M>>(
		self,
		generator: G,
	) -> FlattenUnorderedResult<I, B, M>
	where
		(): Vocabulary<Iri = I, BlankId = B>,
		Self: Sized,
	{
		self.flatten_unordered_with(rdf_types::vocabulary::no_vocabulary_mut(), generator)
	}
}

impl<T: FlattenMeta<I, B, M>, I, B, M> Flatten<I, B, M> for Meta<T, M> {
	fn flatten_with<V, G: id::Generator<V, M>>(
		self,
		vocabulary: &mut V,
		generator: G,
		ordered: bool,
	) -> FlattenResult<I, B, M>
	where
		V: Vocabulary<Iri = I, BlankId = B>,
	{
		T::flatten_meta(self.0, self.1, vocabulary, generator, ordered)
	}

	fn flatten_unordered_with<V, G: id::Generator<V, M>>(
		self,
		vocabulary: &mut V,
		generator: G,
	) -> FlattenUnorderedResult<I, B, M>
	where
		V: Vocabulary<Iri = I, BlankId = B>,
	{
		T::flatten_unordered_meta(self.0, self.1, vocabulary, generator)
	}
}

impl<I: Clone + Eq + Hash, B: Clone + Eq + Hash, M: Clone> FlattenMeta<I, B, M>
	for ExpandedDocument<I, B, M>
{
	fn flatten_meta<V, G: id::Generator<V, M>>(
		self,
		meta: M,
		vocabulary: &mut V,
		generator: G,
		ordered: bool,
	) -> FlattenResult<I, B, M>
	where
		V: Vocabulary<Iri = I, BlankId = B>,
	{
		Ok(Meta(
			self.generate_node_map_with(vocabulary, generator)?
				.flatten_with(vocabulary, ordered),
			meta,
		))
	}

	fn flatten_unordered_meta<V, G: id::Generator<V, M>>(
		self,
		meta: M,
		vocabulary: &mut V,
		generator: G,
	) -> FlattenUnorderedResult<I, B, M>
	where
		V: Vocabulary<Iri = I, BlankId = B>,
	{
		Ok(Meta(
			self.generate_node_map_with(vocabulary, generator)?
				.flatten_unordered(),
			meta,
		))
	}
}

fn filter_graph<T, B, M>(node: IndexedNode<T, B, M>) -> Option<IndexedNode<T, B, M>> {
	if node.index().is_none() && node.is_empty() {
		None
	} else {
		Some(node)
	}
}

fn filter_sub_graph<T, B, M>(
	Meta(mut node, meta): IndexedNode<T, B, M>,
) -> Option<IndexedObject<T, B, M>> {
	if node.index().is_none() && node.properties().is_empty() {
		None
	} else {
		node.set_graph(None);
		node.set_included(None);
		node.set_reverse_properties(None);
		Some(Meta(node.map_inner(Object::node), meta))
	}
}

impl<T: Clone + Eq + Hash, B: Clone + Eq + Hash, M: Clone> NodeMap<T, B, M> {
	pub fn flatten(self, ordered: bool) -> Vec<IndexedNode<T, B, M>>
	where
		(): Vocabulary<Iri = T, BlankId = B>,
	{
		self.flatten_with(&(), ordered)
	}

	pub fn flatten_with<V>(self, vocabulary: &V, ordered: bool) -> Vec<IndexedNode<T, B, M>>
	where
		V: Vocabulary<Iri = T, BlankId = B>,
	{
		let (mut default_graph, named_graphs) = self.into_parts();

		let mut named_graphs: Vec<_> = named_graphs.into_iter().collect();
		if ordered {
			named_graphs.sort_by(|a, b| {
				a.0.with(vocabulary)
					.as_str()
					.cmp(b.0.with(vocabulary).as_str())
			});
		}

		for (graph_id, graph) in named_graphs {
			let (id_metadata, graph) = graph.into_parts();
			let entry = default_graph
				.declare_node(Meta(graph_id, id_metadata.clone()), None)
				.ok()
				.unwrap();
			let mut nodes: Vec<_> = graph.into_nodes().collect();
			if ordered {
				nodes.sort_by(|a, b| {
					a.id()
						.unwrap()
						.0
						.with(vocabulary)
						.as_str()
						.cmp(b.id().unwrap().0.with(vocabulary).as_str())
				});
			}
			entry.set_graph(Some(Entry::new(
				id_metadata.clone(),
				Meta(
					nodes
						.into_iter()
						.filter_map(filter_sub_graph)
						.map(Stripped)
						.collect(),
					id_metadata,
				),
			)));
		}

		let mut nodes: Vec<_> = default_graph
			.into_nodes()
			.filter_map(filter_graph)
			.collect();

		if ordered {
			nodes.sort_by(|a, b| {
				a.id()
					.unwrap()
					.0
					.with(vocabulary)
					.as_str()
					.cmp(b.id().unwrap().0.with(vocabulary).as_str())
			});
		}

		nodes
	}

	pub fn flatten_unordered(self) -> HashSet<StrippedIndexedNode<T, B, M>> {
		let (mut default_graph, named_graphs) = self.into_parts();

		for (graph_id, graph) in named_graphs {
			let (id_metadata, graph) = graph.into_parts();
			let entry = default_graph
				.declare_node(Meta(graph_id, id_metadata.clone()), None)
				.ok()
				.unwrap();
			entry.set_graph(Some(Entry::new(
				id_metadata.clone(),
				Meta(
					graph
						.into_nodes()
						.filter_map(filter_sub_graph)
						.map(Stripped)
						.collect(),
					id_metadata,
				),
			)));
		}

		default_graph
			.into_nodes()
			.filter_map(filter_graph)
			.map(Stripped)
			.collect()
	}
}